Is it possible to invoke a user-defined SQL function from the query interface in EF Core? For example, the generated SQL would look like
select * from X where dbo.fnCheckThis(X.a, X.B) = 1
In my case, this clause is in addition to other Query() method calls so FromSQL() is not an option.
I just managed this with help from this article (H/T #IvanStoev for his comment on the question).
In your DbContext class:
[DbFunction("my_user_function_name")]
public static bool SatisfiesMyUserFunction(int i)
{
throw new Exception(); // this code doesn't get executed; the call is passed through to the database function
}
Note that the function must be in the DbContext class, even though it is static.
Then create a database migration and define the user function in the script.
Usage:
var query = db.Foos.Where(f => MyDbContext.SatisfiesMyUserFunction(f.FieldValue));
Related
I am trying to get an entity with EF by having an initial sql as input.
I tried the context.Entities.SQLQuery method but this returns a DBSet when I require an IQueriable.
I learned that I cannot transform DBSet to IQueryable because the first is already a result of data while the second is the container for the results of a "query" (executed yet or not). Correct me if i'm wrong :)
So I thought that when I write the following lambda I get the resulting query:
db.MyTable.Where(x => x.id == "123")
Becomes:
SELECT * FROM myTable WHERE id = '123'
With this I thought if I can set directly my query without needing to set my lambda...
Is that an option?
Or an alternative?
Thanks!
It's a bit unclear what you mean:
I am trying to get an entity with EF having an initial sql as input.
I'd interpret this, as that you have an SQL statement as input of something and you want to get an entity framework entity that would have this statement (whatever having a statement means)? Not understandable!
I learned that I cannot transform DBSet to IQueryable because the
first (the DbSet) is already a result of data while the second (the IQueryable) is the container for the results of a "query"
NOT!
Every DbSet<T> implements IQueryable<T>, meaning that if you have an object of class DbSet<t>, this object implements all functionality of IQueryable<T>. Just using this IQueryable does not execute the query. The query will only be executed once the first element of the sequence if requested.
using (var dbContext = new MyDbcontext())
{
var result = dbContext.MyItems
.Where(item => ...)
.Select(item => new
{
X = item.Property1,
Y = item.Property2,
...
};
Until here, the first element of the sequence is not asked, the query is not performed yet. No communication with the database was needed (except to create the dbContext object)
Only if you use execution functions like ToList(),Count(), First(), Max(), etc, the query is performed.
You can check this, because you get exceptoin if you do these kind of functions after the using block:
Wrong
IQueryable largeItems;
using (var dbContext = new MyDbcontext())
{
largeItems = dbContext.MyItems
.Where(item => item.Size > 1000);
// query not executed yet
}
int nrOfLargeItems = largeItems.Count();
// exception, query executed after dbContext is disposed
correct
int nrOfLargeItems;
using (var dbContext = new MyDbcontext())
{
var largeItems = dbContext.MyItems
.Where(item => item.Size > 1000);
// query not executed yet
nrOfLargeItems = largeItems.Count();
// the query is performed
}
Conclusion: users of a DbSet<T> inside a dbContext can use the DbSet<T> as if it was an IQueryable<T>, the query will not be executed until you perform any function that needs the first element of the query.
This includes complex functions like Join, GroupBy, OrderBy, etc. You can recognize these functions because MSDN add the following to the remarks section
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach.
This is my simple structure:
public class Post{
public function user(){
return $this->belongsTo(User::class);
}
}
This is how I query (the query has pagination):
$query = Post::query();
$query->with('user');
I want to sort by user.name.
Now, I know that I can go $query->with('user',function(){...order by here});
But I need a detached method that I can run later in the stack.
$query->user->orderBy('name');
$query->user()->orderBy('name');
This https://stackoverflow.com/a/38741988/936651 says that it is possible, but I get the error: Undefined property/method: Illuminate\Database\Eloquent\Builder::$user
Is there any way to sort by related table after using with statement?
Thanks
Your code doesn't work because when you do $query = Post::query(); it will return an instance of the Query Builder, so when you after do $query->user() it gives you an error because the Query Builder does not have the method user(), that method exists in your Post model.
I won't provide any code since you've mentioned you already know how to do the with() way..
I am trying to get Envelope's back from a query. Envelope is defined as follows.
[<CLIMutable>]
type Envelope<'T> = {
Id : Guid
StreamId: Guid
Created : DateTimeOffset
Item : 'T }
MyLibAAS.DataStore.MyLibAASDbContext is a EF DbContext written in c#. When I extend it in f# as follows, I get the error: Only parameterless constructors and initializers are supported in LINQ to Entities.
type MyLibAAS.DataStore.MyLibAASDbContext with
member this.GetEvents streamId =
query {
for event in this.Events do
where (event.StreamId = streamId)
select {
Id = event.Id;
StreamId = streamId;
Created = event.Timestamp;
Item = (JsonConvert.DeserializeObject<QuestionnaireEvent> event.Payload)
}
}
If I return the event and map it to Envelope after the fact, it works fine.
type MyLibAAS.DataStore.MyLibAASDbContext with
member this.GetEvents streamId =
query {
for event in this.Events do
where (event.StreamId = streamId)
select event
} |> Seq.map (fun event ->
{
Id = event.Id
StreamId = streamId
Created = event.Timestamp
Item = (JsonConvert.DeserializeObject<QuestionnaireEvent> event.Payload)
}
)
Why does this make a difference? The Envelope type is not even a EF type.
How F# records work
F# records get compiled into .NET classes with read-only properties and a constructor that takes values of all fields as parameters (plus a few interfaces).
For example, your record would be expressed in C# roughly as follows:
public class Envelope<T> : IComparable<Envelope<T>>, IEquatable<Envelope<T>>, ...
{
public Guid Id { get; private set; }
public Guid StreamId { get; private set; }
public DateTimeOffset Created { get; private set; }
public T Item { get; private set; }
public Envelope( Guid id, Guid streamId, DateTimeOffset created, T item ) {
this.Id = id;
this.StreamId = streamId;
this.Created = created;
this.Item = item;
}
// Plus implementations of IComparable, IEquatable, etc.
}
When you want to create an F# record, the F# compiler emits a call to this constructor, supplying values for all fields.
For example, the select part of your query would look in C# like this:
select new Envelope<QuestionnaireEvent>(
event.Id, streamId, event.Timestamp,
JsonConvert.DeserializeObject<QuestionnaireEvent>(event.Payload) )
Entity Framework limitations
It so happens that Entity Framework does not allow calling non-default constructors in queries. There is a good reason: if it did allow it, you could, in principle, construct a query like this:
from e in ...
let env = new Envelope<E>( e.Id, ... )
where env.Id > 0
select env
Entity Framework wouldn't know how to compile this query, because it doesn't know that the value of e.Id passed to the constructor becomes the value of the property env.Id. This is always true for F# records, but not for other .NET classes.
Entity Framework could, in principle, recognize that Envelope is an F# record and apply the knowledge of the connection between constructor arguments and record properties. But it doesn't. Unfortunately, the designers of Entity Framework did not think of F# as a valid use case.
(fun fact: C# anonymous types work the same way, and EF does make an exception for them)
How to fix this
In order to make this work, you need to declare Envelope as a type with default constructor. The only way to do this is to make it a class, not a record:
type Envelope<'T>() =
member val Id : Guid = Guid.Empty with get, set
member val StreamId : Guid = Guid.Empty with get, set
member val Created : DateTimeOffset = DateTimeOffset.MinValue with get, set
member val Item : 'T = Unchecked.defaultof<'T> with get, set
And then create it using property initialization syntax:
select Envelope<_>( Id = event.Id, StreamId = streamId, ... )
Why does moving the select to a Seq.map work
The Seq.map call is not part of the query expression. It does not end up as part of the IQueryable, so it does not end up compiled to SQL by Entity Framework. Instead, EF compiles just what's inside query and returns you the resulting sequence, after fetching it from SQL Server. And only after that you apply Seq.map to that sequence.
The code inside Seq.map is executed on CLR, not compiled to SQL, so it can call anything it wants, including non-default constructors.
This "fix" comes with a cost though: instead of just the fields you need, the whole Event entity gets fetched from DB and materialized. If this entity is heavy, this may have a performance impact.
Another thing to watch out for
Even if you fix the problem by making Envelope a type with default constructor (as suggested above), you'll still hit the next problem: the method JsonConvert.DeserializeObject can't be compiled to SQL, so Entity Framework will complain about it, too. The way you should do it is fetch all fields to the CLR side, then apply whatever non-SQL-compilable transformations you need.
Using LINQ to Entities, everything that happens in the query computational expression is actually executed within the database engine, which may reside on a remote server. Everything outside of it is executed in the running application on the client.
So, in your first snippet, Entity Framework refuses to execute Envelope<'T>'s constructor because, in order to do so, it would need to translate that into SQL commands for the server. This is plainly not something it can guarantee, because the constructor could potentially contain any sort of side effects and .NET framework code - it could request user input, read files from the client's hard disk, whatever.
What EF can do, in your second snippet, is sending its own POCO event objects back to the client, which is then tasked with Seq.mapping them to your fancy Envelopes, which it can do because it's running on your client machine with access to the full .NET framework.
Addendum: So why are parameterless constructors ok? What if I were to call MsgBox() in a parameterless constructor? I think that parameterless constructors work by having the client construct the objects (without knowing the query results), sending them to the server in serialised form, and having the server just fill the object's properties with the query results.
I haven't actually tested that. But F# record types have no parameterless constructors anyway, so the point is moot in your case.
I'm a SQL guy who's tinkering with Web API and Entity Framework 6 and I keep receiving the error "The operation cannot be completed because the DbContext has been disposed" when I my code is:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
}
But if change my code around a little and try this it works:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
readonly coreEntities coreDB = new coreEntities();
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
My question is why does the second one work but not the first? Is it better practice to have a global connection string or call DBContext explicitly each time?
Your are getting error because you are returning the IQueryable for which Entity framework has yet not executed the query and DbContext has been disposed when that query needs to be executed.
Remember Entity framework will not execute query until collection is initialized or any method that does not support deferred execution. Visit this link for list of Linq deferred execution supported method.
why does the second one work but not the first?
In first code snippet you are returning an instance of IQuerable which has not executed DbQuery and then after it just fires dispose on your context (coreDB). So then after whenever your code iterate over the collection it tries to fire DbQuery but finds that context has already been destroyed so you are getting an error.
In second case when ever you are iterating over the collection coreDB context must be alive so you are not getting an error.
Is it better practice to have a global connection string or call DBContext explicitly each time?
Answer to this question is based on developers taste or his own comforts. You can use your context wrapped within using statements as below:
public IList GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType.ToList();
}
}
As shown in above code snippet if you would use ToList before returning it would execute query before your coreDB got destroyed. In this case you will have to make sure that you returned materialized response (i.e. returned response after executing the DbQuery).
Note: I have noticed most of the people choose the second way. Which targets context as an instance field or property.
Is there a way to get EF CTP5 to create an index when it creates a schema?
Update: See here for how EF 6.1 handles this (as pointed out by juFo below).
You can take advantage of the new CTP5’s ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database.
The best place to invoke SqlCommand method for this purpose is inside a Seed method that has been overridden in a custom Initializer class. For example:
protected override void Seed(EntityMappingContext context)
{
context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}
As some mentioned in the comments to Mortezas answer there is a CreateIndex/DropIndex method if you use migrations.
But if you are in "debug"/development mode and is changing the schema all the time and are recreating the database every time you can use the example mentioned in Morteza answer.
To make it a little easier, I have written a very simple extension method to make it strongly typed, as inspiration that I want to share with anyone who reads this question and maybe would like this approach aswell. Just change it to fit your needs and way of naming indexes.
You use it like this: context.Database.CreateUniqueIndex<User>(x => x.Name);
.
public static void CreateUniqueIndex<TModel>(this Database database, Expression<Func<TModel, object>> expression)
{
if (database == null)
throw new ArgumentNullException("database");
// Assumes singular table name matching the name of the Model type
var tableName = typeof(TModel).Name;
var columnName = GetLambdaExpressionName(expression.Body);
var indexName = string.Format("IX_{0}_{1}", tableName, columnName);
var createIndexSql = string.Format("CREATE UNIQUE INDEX {0} ON {1} ({2})", indexName, tableName, columnName);
database.ExecuteSqlCommand(createIndexSql);
}
public static string GetLambdaExpressionName(Expression expression)
{
MemberExpression memberExp = expression as MemberExpression;
if (memberExp == null)
{
// Check if it is an UnaryExpression and unwrap it
var unaryExp = expression as UnaryExpression;
if (unaryExp != null)
memberExp = unaryExp.Operand as MemberExpression;
}
if (memberExp == null)
throw new ArgumentException("Cannot get name from expression", "expression");
return memberExp.Member.Name;
}
Update: From version 6.1 and onwards there is an [Index] attribute available.
For more info, see http://msdn.microsoft.com/en-US/data/jj591583#Index
This feature should be available in the near-future via data annotations and the Fluent API. Microsoft have added it into their public backlog:
http://entityframework.codeplex.com/workitem/list/basic?keywords=DevDiv [Id=87553]
Until then, you'll need to use a seed method on a custom Initializer class to execute the SQL to create the unique index, and if you're using code-first migrations, create a new migration for adding the unique index, and use the CreateIndex and DropIndex methods in your Up and Down methods for the migration to create and drop the index.
Check my answer here Entity Framework Code First Fluent Api: Adding Indexes to columns this allows you to define multi column indexes by using attributes on properties.