Linq where keyword vs. Where extension and Expression parameters
Passing in an Expression to a Linq query behaves differently 开发者_开发技巧depending on syntax used, and I wonder why this is the case.
Let's say I have this very generic function
private IEnumerable<Company>
GetCompanies(Expression<Func<Company, bool>> whereClause)
The following implementation works as expected
private IEnumerable<Company>
GetCompanies(Expression<Func<Company, bool>> whereClause)
{
return (from c in _ctx.Companies.Where(whereClause) select c);
}
But this next implementation does not compile (Delegate 'System.Func' does not take 1 arguments)
private IEnumerable<Company>
GetCompanies(Expression<Func<Company, bool>> whereClause)
{
return (from c in _ctx.Companies where whereClause select c);
}
Obviously I can just use the first syntax, but I was just wondering why the compiler does not treat the where keyword the same as the Where extension?
Thanks, Thomas
The syntax for a query expression involving a where
clause is (simplifying the complete grammar)
from identifier in expression where boolean-expression select expression
whereClause
is not a boolean expression. To recitify this, you have to say
from c in _ctx.Companies where whereClause.Compile()(c) select c;
Note that if whereClause
were a Func<Company, bool>
you could get away with
from c in _ctx.Companies where whereClause(c) select c;
Note that
from x in e where f
is translated mechanically by the compiler into
(from x in e).Where(x => f)
I say mechanically because it performs this translation without doing any semantic analysis to check validity of the method calls etc. That stage comes later after all query expressions have been translated to LINQ method-invocation expressions.
In particular,
from c in _ctx.Companies where whereClause select c
is translated to
_ctx.Companies.Where(c => whereClause).Select(c)
which is clearly nonsensical.
The reason that
from c in _ctx.Companies.Where(whereClause) select c
is legit is because IEnumerable<Company>.Where
has an overload accepting a Func<Company, bool>
and there is an implicit conversion from an Expression<Func<Company, bool>>
to a Func<Company, bool>
.
You can actually shorten the whole thing to:
private IEnumerable<Company>
GetCompanies(Expression<Func<Company, bool>> whereClause)
{
return _ctx.Companies.Where(whereClause);
}
When you use the LINQ syntax, the code in the where
clause is translated into an Expression<>
, which represents a code tree. When you accept Expression<Func<Customer, bool>>
, you are saying that your method accepts a code tree, which is converted from C# code by the compiler.
Since you already have the code tree, you have to pass it directly to the Where()
method, rather than using LINQ syntax.
The difference is that in sql-like where it expects expression that evaluates to bool. But in Where method the type of expression can be the delegate.
to force second to work you can change to whereClause.Compile()(c)
or change parameter to Func<Company, bool> whereClause
and whereClause(c)
精彩评论