Is it possible to extend DbFunctions, assuming you are using existing DbFunctions as helper methods. I am essentially rewriting the exact same line of sql code again and again. Are there any alternatives?
Update:
Here is an example of what I'm trying to do, but I want to define my own Add function, not use one that I've constructed in my database
var locations = context.Data.Where(e => Functions.Add(e.X, e.Y) >= 10)
Yes of course you can extend it. Class DbFunctions only contains code that helps the provider of your IQueryable to translate the expression into SQL.
An IEnumerable holds all code to create an Enumerator for you upon request. The Enumerator represents a sequence. Upon request it will give you the first element of the sequence, and once you've got an element it will give you the next one (provided there is one).
An IQueryable works differently. Usually an IQueryable is not meant to be performed by your process, but for instance a database, a remote web site, a CSV file controller, etc.
That is why you need to tell an object that produces IQueryables for which process it must create the IQueryable. In case of Entity Framework you inform the DbContext which database to use.
The IQueryable object holds an Expression to be performed and a Provider. The provider knows which process will perform the query. It also knows how to translate the Expression into the format that the other process understands. Quite often this will be SQL.
If you investigate the remarks section of the MSDN descriptions of IQueryable functions like Where, GroupBy, Select, you'll see that most of these functions will only change the Expression.
As long as you don't ask for the Enumerator, usually implicitly by asking for the first element of a sequence, like in ToList, foreach, FirstOrDefault, etc, the Provider has nothing to do.
But once you ask for the Enumerator, the Expression will be translated by the Provider, who will use the translation to query the data from the other process and create an Enumerator object that can give you the first element of the sequence, and the next ones.
DbFunctions are used when the Provider translates the Expression into SQL. If you create a Queryable with DbFunctions and in your debugger look at the created Expression, you'll still find the used DbFunctions.
The DbFunctions only translate the input into SQL. If does not perform the query itself. The translation is done in local memory.
Having understood this, you can use any function as long as it only changes the Expression into new Expressions into formats that your provider understands.
This means you can't use any of your own functions, or classes. There are even several LINQ functions you can't use
See supported and non-supported LINQ methods
However, if your extension functions input an IQueryable and output an IQueryable, then your extension function will only change the Expression. As long as you fill the Expression with supported LINQ methods you're fine
So if you want to extend IQueryable with a function that returns an IQueryable containing only the invoice that are due to day:
public static IQueryable<Invoice> WhereDueToday(this IQueryable<Invoice> invoices)
{ // returns all invoices that must be paid today
return invoices
.Where(invoice => DbFunctions.TruncateTime(invoice.DueDate) == DateTime.Today);
}
Usage:
IQueryable<Invoice> invoices = dbContext.Invoices
.Where(invoice => ..);
IQueryable<Invoice> invoicesDueToDay = invoices
.WhereDueToday();
You can define a method that returns an Expression and use that in your where clause. Since you want to pass different properties of the object in you can't just write one expressions
public Expression<Func<T, bool>> MyFunc<T>(Expression<T, int> property1, Expression<T, int> property2, int greaterThan)
{
// Build expression tree
}
I realise "Build Expression tree" isn't hugely useful, but if you don't really want to do add writing out the code to build "add" isn't going to help you either.
If there are just a couple of combinations it might be easier to just hard code for those
public Expression<Func<T, bool>> MyFunc<T>(PropertiesEnum p, int greaterThan)
{
switch(p)
{
case (p.XandY):
return item => (item.X + item.Y) > greaterThan;
case (p.XandZ):
return item => (item.X + item.Z) > greaterThan;
case (p.YandZ):
return item => (item.X + item.Z) > greaterThan;
// other cases
}
}
You would call this like:
var locations = context.Data.Where(MyFunc(PropertiesEnum.XandY, 10));
Related
I am using entity framework and I want to execute a query and would like to know which is the besr way to execute a query. Which is best practice and why, and which one is more per-formant.
Option 1)
return
this.Storage.Customer.OfType<Preferred>()
.Include("Order")
.Where("it.Id = #customerId AND it.CustomerType = #cusType", new ObjectParameter("customerId ", customerId), new ObjectParameter("cusType", (int)cusType))
.Execute(MergeOption.OverwriteChanges)
.SingleOrDefault();
OR
return
this.Storage.Customer.OfType<Preferred>()
.Include(b => b.Order)
.Where(cust => cust.Id == customerId && cust.CustomerType== (int)cusType)
.SingleOrDefault();
The second question is why in option 2 us the .Execute not available? It appears red.
Thanks in advance.
The performance difference should be negligible compared with the actual data access, but you need to measure it to determine for sure.
Include with a lambda just uses reflection to get the property name then calls the version with a string parameter, so the only overhead is parsing the expression. (This is an implementation detail, however, so it is subject to change)
The benefit of using a lambda is type safety - using the wrong property name in a lambda will break the build, but using the wrong string will only fail at run-time.
The reason Execute is not available is because Include with a lambda parameter is an extension method on IQueryable<T> that returns an IQueryable<T> in order to chain methods like Where. Include with a string parameter is a method on ObjectQuery<T> that returns an ObjectQuery<T>. Execute is a method on ObjectQuery<T>, not IQueryable<T> so it is not available when you use IQueryable methods.
Plot:
I have a class implemented as a facade around Entity Framework DB context. It developed to maintain backward compatibility, it mimics class with same public interface, but uses DTOs instead of EF entities.
Problem:
I have next method inside class described above. See code below:
public IQueryable<T> FindBy<T>(Expression<Func<T, Boolean>> predicate) where T : BaseDto {
//GetDestinationType takes source type of some declared mapping and returns destination type
var entityType = Mapping.Mapper.GetDestinationType(typeof (T));
var lambda = Expression.Lambda(predicate.Body, Expression.Parameter(entityType));
// dbContext declared as class field and initialized in constructor
var query = dbContext.Set(entityType).Where(lambda); // <-- Cannot use non-generic expression/lambda
return query.ProjectTo<T>(mapper.ConfigurationProvider); }
I need to take Expression using DTOs as In parameter and return IQueryable where T : BaseDto as a result
I need to convert input predicate to the same predicate using EF entities as In parameter
I need to filter non-generic EF DbSet with help of dynamically created Expression (predicate)
Main question
Is it possible to filter non-generic EF DBSet with help of dynamically created Expression (predicate)
Please give me some glue or further directions if I need to use some other approach.
Problem has been solved. Solution was quite obvious. Instead of
var query = dbContext.Set(entityType).Where(lambda);
I can write
var query = dbContext
.Set(entityType)
.ProjectTo<T>(mapper.ConfigurationProvider)
.Where(predicate);
where predicate is the input parameter of FindBy() method.
This code will be successfully compiled and, what is more important, EF will build optimal query to the Database which will include Where() clause in the query body, so it won't take full set of records from DB side.
I am using Entity Framework 4.3.1 with a Code First approach. Also, I am using LinqKit so as to use the PredicateBuilder.
If I have tables like so:
Location, TimeZone (Many:1)
..and I wish to have something like so:
Expression<Func<TimeZone, bool>> pred = PredicateBuilder.True<TimeZone>();
pred = pred.And(tz => tz.TimeZoneCode == "EST");
List<Location> locations = context.Locations
.AsExpandable().Where(pred)
.Select(loc => loc).ToList();
This does not work, because the predicate is built to accept a TimeZone, but the Where() method receives a Location.
I can rewrite the predicate like so, but I do not want to, because I want to have a predicate factory that creates predicates for specific types (I do not want to use the Navigator properties in this manner):
Expression<Func<Location, bool>> pred = PredicateBuilder.True<Location>();
pred = pred.And(loc => loc.TimeZone.TimeZoneCode == "EST");
What syntax could I use (if any) to use the predicate as constructed in the first example, where it takes a TimeZone, rather than have it take a Location and walk the tree via the navigation properties (as this is less reusable). It would be nice if there was a way to leverage the knowledge that EF has about the navigation properties in the first place, and be able to use a predicate scoped to the type of the navigation property.
After about a week's worth of struggling, I found out that you can in fact do this. The steps are:
Define a Predicate that is the query for your inner property (subPredicate)
Invoke that subPredicate from within another Predicate (predicate), against the property of the parent object.
Expand your predicate when using it in your Where clause.
Here's the revised code for your example:
var subPredicate = PredicateBuilder.True<TimeZone>();
subPredicate = subPredicate.And(tz => tz.TimeZoneCode == "EST");
var predicate = PredicateBuilder.True<Location>();
predicate = predicate.And(l => subPredicate.Invoke(l.TimeZone));
List<Location> locations = context.Locations
.AsExpandable().Where(pred.Expand())
.Select(loc => loc).ToList();
Just to update this: it turns out that the intent of these kinds of predicates is to filter the primary entity. The mental concept is: decide which entities you would like to return, and return them. EF is apparently not designed for this kind of deep predicate application on child entities.
One person (I don't remember where) made a great point: If the children were pre-loaded, you would not expect a partially-loaded collection. Thus, it would not make sense to have an Invoice entity, for example, but only half of the Invoice Lines.
What I wanted to achieve was more along the lines of EXISTS() or IN(), where you could say "Give me all Invoices where there are Invoice Lines for Product Types 'Nuts' and 'Bolts'". This is feasible, but you may have to apply LINQ or compose your own object. The intent of EF out of the box is to hand you the Invoice, and then you may lazily or eagerly load the Invoice Lines, but not using them as a filter straight from the database.
There are some constructs that I saw to accomplish something close, but the syntax gets very unmanageable very quickly.
Don't fight city hall.
Basically I'd be looking to implement a method like this.
IQueryAble GetQuery<T>(Entities db) or extension method Entities.GetQuery<T>()
This way you could do things like this
public IQueryable<T> GetAll()
{
return yourEntityClasses.GetQuery<T>();
}
which would return a SELECT * FROM query expression and obviously from there you could make addtional generic methods for sorting, pagination, where expressions, etc on top of this would save you from having to repeat the code for these methods for each table. I know SubSonic3 does a very good job of this, but was trying to duplicate some of the functionality in an EntityFramework project I am working on. Only thing I see in EF is CreateQuery and ObjectQuery but both of those require you to pass a querystring in which would require you to know the table name.
Well it is possible to get the string that you need to put into the CreateQuery method automatically.
Basically it is just string.Format("[{0}]",entitySetName);
How do you get the entity set name, assuming you have don't use something called MEST, most people don't, you can use some a functions I wrote in this Tip to get the EntitySet for T and from that the name.
Once you do that it should be pretty trivial for you to write the Extension Method
i.e.
public static IQueryable<T> GetAll<T>(this ObjectContext context)
{
var wkspace = context.MetadataWorkspace;
EntitySet set = wkspace
.GetEntitySets(wkspace.GetCSpaceEntityType<T>())
.Single();
return context.CreateQuery<T>(string.Format("[{0}]",set.Name);
}
See the above tip for the other functions used.
Hope this helps
Alex
I am using the entity framework and I'm having a problem with "re-finding" objects I just created... basically it goes like this:
string theId = "someId";
private void Test()
{
using(MyEntities entities = new MyEntities())
{
EntityObject o = new EntityObject();
o.Id = theId;
entities.AddToEntityObject(o);
CallSomeOtherMethod(entities);
}
}
void CallSomeOtherMethod(MyEntities ents)
{
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId);
if(search == null)
{
Console.WriteLine("wha happened???");
}
}
(no guarantee the code works btw - it's all from my head)
Why doesn't the query "find" the EntityObject that was just created?
If I call SaveChanges() after the AddToEntityObject it works (which doesn't surprise me) but why doesn't it pull from the cache properly?
I'm still green on this stuff so I'm hoping that there's some really easy thing that I'm just overlooking...
Thanks
The newly added object is in the local DataSource, since it's not persisted yet in the database,
so you may say:
EntityObject search = ents.EntityObject.FirstOrDefault(o => o.Id == theId) ??
ents.EntityObject.Local.FirstOrDefault(o => o.Id == theId);
This happens because ents.EntityObject.WhatEver always queries the datasource. This is a design decision. They do it this way, because else they would have to execute the query against the datasource, against the local cache and then merge the results. As one of the developers pointed out in a blog (cannot remember where exactly) they were unable to handle this consistently.
As you can imagine there are a lot of corner an edge cases you have to handle properly. You could just find a id you created locally, created by someone else in the database. This would force you to be prepared to handle conflicts on (almost) every query. Maybe they could have made methods to query the local cache and methods to query the datasource, but that is not to smart, too.
You may have a look at Transparent Lazy Loading for Entity Framework. This replaces the normal code generator and you get entities that populate their related entity collections and entity references automatically on access. This avoids all the
if (!Entity.ReleatedEntities.IsLoaded)
{
Entity.RelatedEntities.Load();
}
code fragments. And you can query the collections because they are always implicitly loaded. But this solution is not perfect, too. There are some issues. For example, if you create a new entity and access a collection of related entities, you will get an exception because the code is unable to retrieve the related entities from the database. There is also an issue concerning data binding and may be some more I am not aware of.
The good thing is that you get the source code and are able to fix the issues yourself and I am going to examine the first issue if I find some time. But I am quite sure that it will not be that easy to fix, because I expect some case were just not hitting the database if the entity has just been created is not the expected behavior.
I was in the same situation. I wrote this extension method that at least for me solves the problem (I don't have issues with i.e conflicts in my context...)
public static IEnumerable<T> WhereInclAdded<T>(this ObjectSet<T> set, Expression<Func<T, bool>> predicate) where T : class
{
var dbResult = set.Where(predicate);
var offlineResult = set.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(entry => entry.Entity).OfType<T>().Where(predicate.Compile());
return offlineResult.Union(dbResult);
}
The extension method bellow is to DbSet<>
public static T TryAttach<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate) where T : class
{
T found = dbSet.Local.SingleOrDefault(predicate.Compile());
if (found == null) dbSet.Attach(entity);
return found ?? entity;
}
How to use:
contextInstance.MyEntity.TryAttach(entityInstance, e => e.ID == entityInstance.ID);
btw: I love generics!
I have recently struggled with this same question. I'm posting this answer 2 years after the question was asked in hopes that this bit of code may help someone searching for an answer.
I have basically implemented an extension method (as suggested by Alex James) called "Find" that operates in the same way that "Where" does, but "Find" also checks the ObjectContext to see if there are any Added entities that satisfy the given predicate. This allows you to find an entity even if it hasn't been saved to the database yet.
Find returns an IQueryable(of T) so that you can use it just like any other LINQ operator.
<Extension()>
Public Function Find(Of T As Class)(ByVal OSet As ObjectSet(Of T), _
ByVal predicate As Expression(Of Func(Of T, Boolean))) _
As System.Linq.IQueryable(Of T)
'Check the object context for Added objects first.
Dim AddedContextObjects = OSet.Context.ObjectStateManager _
.GetObjectStateEntries(EntityState.Added) _
.Select(Function(entity) entity.Entity).OfType(Of T)()
Dim Cpredicate = predicate.Compile
Dim MatchingObjects As New List(Of T)
For Each TObj As T In AddedContextObjects
If Cpredicate.Invoke(TObj) Then
MatchingObjects.Add(TObj)
End If
Next
'Now include a query to retrieve objects from the DB.
Dim DBObjects = OSet.Where(predicate)
If MatchingObjects.Count > 0 Then
'We found some added objects in the context.
'We want to return these objects as well as any Objects in DB
'that satisfy the predicate.
Return MatchingObjects.Union(DBObjects).AsQueryable
Else
'We didn't find any added objects in the context,
'so we just return the DB query.
Return DBObjects
End If
End Function
You have a number of options. You could extend the ObjectContext with another partial class to make your own mechanism for retrieving recently Added information.
Or you could just put an extension method on the ObjectContext that looks through the ObjectContext.ObjectStateManager looking for 'added' ObjectStateEntries, and then use LINQ to Objects to find what you are looking for.
Entity Framework 6
As per EF Docs Dbset always query against the database.
Note that DbSet and IDbSet always create queries against the database
and will always involve a round trip to the database even if the
entities returned already exist in the context. A query is executed
against the database when:
It is enumerated by a foreach (C#) or For Each (Visual Basic)
statement. It is enumerated by a collection operation such as ToArray,
ToDictionary, or ToList. LINQ operators such as First or Any are
specified in the outermost part of the query. The following methods
are called: the Load extension method on a DbSet,
DbEntityEntry.Reload, and Database.ExecuteSqlCommand. When results are
returned from the database, objects that do not exist in the context
are attached to the context. If an object is already in the context,
the existing object is returned (the current and original values of
the object's properties in the entry are not overwritten with database
values).
When you perform a query, entities that have been added to the context
but have not yet been saved to the database are not returned as part
of the result set. To get the data that is in the context, see Local Data
If a query returns no rows from the database, the result will be an
empty collection, rather than null.
Below is a simple snippet with local data:
await dbContext.Entity
.Where(e => e.Title.Contains("Text"))
.LoadAsync();
var locaEntities = dbContext.Entity.Local;
dbContext.Entity.Add(new Entity {});
// call save post atomic operation is finished.
await dbContext.SaveChangesAsync();