Multiple func parameters for LINQ - EF - c#

I was fiddling around with the linq its Func parameter(on entity framework). Then I found out this behaviour
var idMatchQuery = new Func<MyClass, bool>(x => x.Id == someId);
var statusMatchQuery = new Func<MyClass, bool>(x => x.Status == someStatus);
/// works
var a = myClassEntity.FirstOrDefault(idMatchQuery);
/// doesn't work
var b = myClassEntity.FirstOrDefault(p => idMatchQuery(p) && statusMatchQuery(p));
/// doesn't work
var c = myClassEntity.FirstOrDefault(p => idMatchQuery(p) && p.Status == 1);
It throws UnsupportedOperationException since the EF does not recognize those queries. I could have accept it much easier if none of the above were not working. But it bugs me when it works with one and only one Func query, but not with combinations with other queries.
I'm sure there exist an explanation for it, but I guess my search terms were too naive for the answer I'm looking for.
What is the explanation for this behaviour?

It is caused that, EF should translate your predicate to TSQL language. You can check, that parameter of FirstOrDefault method is not Func<T, bool>, but instead Expression<Func<T, bool>>, because last one give us oppotunity to parse it and translate to TSQL. When you use two Func<T, bool> or Func<T, bool> with simple condition EF can't translate and parse it to TSQL due to Expression<Func<T, bool>> inner stuff and features complexity, that is why EF keep this predicates at origin state and send to server as it was written at first, as a result - UnsupportedOperationException. So, for EF - parse first predicate much more easily than other two.
Conclusion: it is caused by features and methodology of translation C# predicates from Expression<Func<T,bool>> to TSQL, because of it's sometimes enough high complexity.

Related

NHibernate Futures using Linq to query count

I have this method below that queries over database to get for instance User, Orders or any entity for that matter. I want to know if the below query is optimal or do i have to tweak that.
Does the query do where, only after storing all records in the memory ?
Is there a way i can specify Expression for the filtering rather than Func<> ?
What it the optimal way to get Count, specifying a condition/where without much memory consumption?
C# Sample Code
public IList<TEntity> Find(Func<TEntity, bool> predicate)
{
//Criteria for creating
ICriteria filterCriterea = _unitOfWork.CurrentSession.CreateCriteria(typeof(TEntity));
//filtered result
return filterCriterea.Future<TEntity>().Where(predicate).ToList();
}
The are several things that you need to fix:
1. Ask for an Expression, not a Func
Your Find method specifies that it wants a Func. If you want an Expression, you have to say so:
public IList<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
2. Use LINQ or QueryOver instead of ICriteria
Assuming you're not using an ancient version of NHibernate that doesn't have LINQ or QueryOver (oddly, there are a LOT of people still using NHibernate 1.2.1 - I can't figure out why)...
ICriteria doesn't understand Expressions. Replace CreateCriteria(typeof(TEntity)) with Query<TEntity>() or QueryOver<TEntity>() depending on which query syntax you prefer.
3. Where should go before Future
You need to move the Where before the Future. Future is used to batch several queries together so that they all get executed in one round trip to the database as soon as your code tries to evaluate the results of one of the "Future" queries. At the point your above code calls Future, the query only consists of CreateCriteria(typeof(TEntity)), which just tells it which table to query against, like so:
select * from TEntity
If you want a where clause in there, you have to switch those method calls around:
filterCriterea.Where(predicate).Future<TEntity>()
This should give you a SQL query like:
select * from TEntity where ...
It is very important to learn the difference between IQueryable and IEnumerable. You should notice that Query returns an IQueryable, whereas Future returns an IEnumerable. Manipulating an IQueryable using Expressions will result in changes to the SQL that gets executed. Manipulating an IEnumerable using a Func just changes the way you're looking at the in-memory data.
4. ToList immediately after Future negates benefit of Future
ToList iterates over the collection that is passed to it and puts each element in a list. The act of iterating over the collection will cause the Future query to be immediately executed, not giving you any chance to batch it together with other queries. If you must have a list, you should just omit the Future. I think however, that it would be a better idea to change your method to return an IEnumerable, which would give you the ability to batch this query together with other queries. Like so...
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
var query = _unitOfWork.CurrentSession.Query<TEntity>();
return query.Where(predicate).Future<TEntity>();
}
In order to take advantage of the query batching, you are probably going to have to rearrange the code that calls this Find method. For example, instead of...
foreach (var openThing in thingRepository.Find(x => x.Status == Status.Open))
CloseIt(openThing);
foreach (var negativeWhatsit in whatsitRepository.Find(x => x.Amount < 0))
BePositive(negativeWhatsit);
... you should do this:
var openThings = thingRepository.Find(x => x.Status == Status.Open);
var negativeWhatsits = whatsitRepository.Find(x => x.Amount < 0);
// both queries will be executed here in one round-trip to database
foreach (var openThing in openThings)
CloseIt(openThing);
foreach (var negativeWhatsit in negativeWhatsits)
BePositive(negativeWhatsit);

How can I use a Predicate<T> in an EF Where() clause?

I'm trying to use predicates in my EF filtering code.
This works:
IQueryable<Customer> filtered = customers.Where(x => x.HasMoney && x.WantsProduct);
But this:
Predicate<T> hasMoney = x => x.HasMoney;
Predicate<T> wantsProduct = x => x.WantsProduct;
IQueryable<Customer> filtered = customers.Where(x => hasMoney(x) && wantsProduct(x));
fails at runtime:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
I can't use the first option, as this is a simple example, and in reality, I'm trying to combine a bunch of predicates together (with and, not, or, etc.) to achieve what I want.
How can I get the EF Linq provider to "understand" my predicate(s)?
I get the same result if I use a Func<T, bool>. It works with an Expression<Func<T>>, but I can't combine expressions together for complex filtering. I'd prefer to avoid external libraries if possible.
UPDATE:
If this cannot be done, what options do I have? Perhaps expressions be combined / or'ed / and'ed somehow to achieve the same effect?
Expression<Func<Customer, bool>> hasMoney = x => x.HasMoney;
Expression<Func<Customer, bool>> wantsProduct = x => x.WantsProduct;
IQueryable<Customer> filtered = customers.Where(hasMoney).Where(wantsProduct);
Use Expression<T> to leave x => x.HasMoney as an expression tree, and not compile it to a .NET method
Use Expression<Func<Customer, bool>> rather than Expression<Predicate<Customer>>, because that's what Queryable.Where expects
Pass it directly in .Where, combining them using multiple .Where calls instead of &&.
It's possible to get more complex conditions (including not, or, etc.) working by rewriting them using .Union, .Except, etc.
An alternative is to use LINQKit's AsExpandable:
Expression<Func<Customer, bool>> hasMoney = x => x.HasMoney;
Expression<Func<Customer, bool>> wantsProduct = x => x.WantsProduct;
IQueryable<Customer> filtered = customers.AsExpandable().Where(x => hasMoney.Invoke(x) && wantsProduct.Invoke(x));
Unfortunately there's no way to use Predicate<T> in EF linq since it's impossible to map it on SQL query. This can be done with Expressions only because they can be parsed and converted to SQL.
In fact there are 4 language features that made linq possible:
Extension methods
Type inference
Closures
and for linq2sql especially
Expressions
UPDATE:
The possible solution is building expressions programmatically. How to: Use Expression Trees to Build Dynamic Queries

Why does EntityFramework's LINQ parser handle an externally defined predicate differently?

I'm using Microsoft's Entity Framework as an ORM and am wondering how to solve the following problem.
I want to get a number of Product objects from the Products collection where the Product.StartDate is greater than today. (This is a simplified version of the whole problem.)
I currently use:
var query = dbContext.Products.Where(p => p.StartDate > DateTime.Now);
When this is executed, after using ToList() for example on query, it works and the SQL created is effectively:
SELECT * FROM Product WHERE StartDate > (GetDate());
However, I want to move the predicate to a function for better maintainability, so I tried this:
private Func<Product, bool> GetFilter()
{
Func<Product, bool> filter = p => p.StartDate > DateTime.Now;
return filter;
}
var query = dbContext.Products.Where(GetFilter());
This also works from a code point of view insofar as it returns the same Product set but this time the SQL created is analogous to:
SELECT * FROM Product;
The filter is moved from the SQL Server to the client making it much less efficient.
So my questions are:
Why is this happening, why does the LINQ parser treat these two formats so differently?
What can I do to take advantage of having the filter separate but having it executed on the server?
You need to use an Expression<Func<Product, bool>> in order for it to work like you intend. A plain Func<Product, bool> tells LINQ that you want it to run the Where in MSIL in your program, not in SQL. That's why the SQL is pulling in the whole table, then your .NET code is running the predicate on the entire table.
You are returning a Func, but to inject the predicate into the SQL, LINQ requires an expression tree. It should work if you change the return type of your method (and of your local variable, of course) to Expression<Func<Product, bool>>.
Since in second case filter func maybe arbitrary LINQ to EF can't parse you filter to SQL and has to resolve it on client side.

Entity Framework 4.1 Code First - Include is being ignored when using LinqKit PredicateBuilder

I am using Entity Framework 4.1 Code First and am also using the PredicateBuilder so that I can build predicate expressions across multiple Specification classes (using the Specification pattern). I am able to properly build a predicate and apply it to a DbSet, and the data I get is what I expect. However, no matter what I try, it's always lazy loading. This is a simple example of how I'm building the predicate and applying it.
IQueryable<HairColor> hairColorQuery = Context.Set<HairColor>().AsExpandable();
Expression<Func<Parent, bool>> parentPredicate = PredicateBuilder.And(PredicateBuilder.True<Parent>(), p => p.NameLast.StartsWith("V")).Expand();
Expression<Func<HairColor, bool>> hairColorPredicate = PredicateBuilder.And(PredicateBuilder.True<HairColor>(), h => h.Parents.AsQueryable().Any(parentPredicate));
HairColor[] hairColors = hairColorQuery.Where(hairColorPredicate).Include(h => h.Parents).ToArray();
Like I said above, I'm getting the data back that I want, but it ignores the Include.
Does anyone have any ideas?
Hi jumping in late on this, but had the same issue with using an extension Include method when using LinqKits predicate builder. The problem as referred to in the previous answer is that casting LinqKits ExpandableQuery to ObjectQuery (as required by the Include extension) results in null.
However found this link http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ which is a Predicate builder which doesn't use AsExpandable to perform the search and hence the Include method can be used on it. Although the solution above also worked for me this different predicate builder allowed me to keep my code cleaner/more consistent
It's probably changing the query shape. Try this workaround
HairColor[] hairColors = hairColorQuery.Where(hairColorPredicate)
.Select(hc => new
{
HairColor = hc,
Parents = hc.Parents // eager load
})
.AsEnumerable()
.Select(hc => hc.HairColor);

Why do navigation properties in linq to entities within a subquery not implement IQueryable?

I've recently noticed something odd, navigation properties on an Entitiy do not implement IQueryable,
while as far as i know retrieving a single entity and then chaining operators on navigation property may result in many (1 per item) calls to the database, from the generated code i've seend having one large query with subqueries results (when possible) in a single large query.
However i'm wondering about how it works because the overloads available in a subquery are the same as the ones which would be called on the navigation property outside of the subquery.
Question 1: my guess is that , even if it's on IEnuerable, while the any call itself is part of the expression passed to where, it's then build into the single expression tree generated in where and parsed there and not as a separate expression (since it isn't an expression by itself), is this correct?
Qestion 2: If #1 is correct, how does this work if you create code outside from the expression which may only be made in a delegate at runtime (for example, how would that work if i passed PredicateExprEntityTwo.Compile(), in q5, would that not compile and fail at runtime because the compiler doesn't have knowledge of the usage of the func within an expression at compile time?
Question 3: Assuming i've got #1 and #2 correct, what's the advantage of this design vs taking an expression there? The disadvantage i've ran into is that for business validation i was thinking of having a set of predicates with business logic incorporated in them to filter the same type of entities in many places in a program, however i may want to use those in subqueries too and assuming #2 is right it may be unfeasible to reuse the same one?
I'm sorry if the questions sound a bit confusing but i've only noticed last week that i had IEnumerables overload called in subqueries yet still a single EF query as an output and i'm very curious about how this may work.
public class Class1
{
void Test()
{
Func<Entity1, bool> PredicateFuncEntityOne = i => i.Id == 2;
Expression<Func<Entity1, bool>> PredicateExprEntityOne = i => i.Id == 2;
Func<Entity2, bool> PredicateFuncEntityTwo = i => i.Id == 2;
Expression<Func<Entity2, bool>> PredicateExprEntityTwo = i => i.Id == 2;
using (var Context = new TestModelContainer())
{
// Works as this expects an expression
var q1 = Context.Entity1Set.Where(PredicateExprEntityOne);
// Works but would call the IEnumerable version
var q2 = Context.Entity1Set.Where(PredicateFuncEntityOne);
// This compiles, any on item.Entity2 expects a func on IEnumerable, not IQueryable overload
var q3 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateFuncEntityTwo));
// This fails for the reason mentioned above
var q4 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateExprEntityTwo));
// Does this work and if so how is it possible?
var q5 = Context.Entity1Set.Where(item => item.Entity2.Any(PredicateExprEntityTwo.Compile()));
}
}
}
Q1 : I too think this is right.
Q2 : Yes, if you pass Func or any delegate into expression, then compiling of query will fail.
I am not sure about Q3.
But some time back, I had similiar problem while playing around in EF. And I found that casting those relational properties to IQueryable and calling methods Expressions supprisingly worked. But it might be a fluke.

Categories

Resources