views:

88

answers:

4

Hi,

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

A: 

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)

Andrey
A: 

where statement expects an expression and wraps it around to make it an anonymous function. whereClause, however, is not expression but an anonymous function already. So you get a compiler error.

ssg
+4  A: 

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>.

Jason
If you were to accept Func<Company, bool> as an argument instead of Expression<Func<Company, bool>>, then you could have used Predicate<Company> as the parameter type.
Frode N. Rosand
Yeah this is a simplified example only to focus on the 'where' differences. The reason for using a Expression<Func<Company, bool>> is that I would want the where to be executed on the data base and not in memory.
Thomas Scheelhardt
+2  A: 

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.

Bryan Watts
Yes I do realize that, my example was made to spell out where the difference occurs, i.e. where vs. .Where.Thanks though :).
Thomas Scheelhardt