开发者

Linq where keyword vs. Where extension and Expression parameters

开发者 https://www.devze.com 2022-12-21 16:20 出处:网络
Passing in an Expression to a Linq query behaves differently 开发者_开发技巧depending on syntax used, and I wonder why this is the case.

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)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号