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

c# entity-framework linq linq-expressions

Question

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?

1
12
11/15/2012 1:34:36 AM

Accepted Answer

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));
10
11/15/2012 8:59:26 AM

Popular Answer

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:

  1. Extension methods
  2. Type inference
  3. Closures
    and for linq2sql especially
  4. Expressions

UPDATE:
The possible solution is building expressions programmatically. How to: Use Expression Trees to Build Dynamic Queries



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow