views:

191

answers:

2

This works:

(from o in Entities.WorkOrderSet
 select o)
.Where(MyCustomMethod);

This does not:

(from o in Entities.WorkOrderSet
 select o)
.Where(o => MyCustomMethod(o));

([Edit] Even without new, it doesn't work)

I understand why the second doesn't work - but why in the world does the first work!? Shouldn't I get a "LINQ-to-Entities does not recognize the method..." at runtime, like with the second?

For reference, here is MyCustomMethod

public bool MyCustomMethod(WorkOrder workOrder)
{
    return !workOrder.WorkOrderNum.StartsWith("A", StringComparison.CurrentCultureIgnoreCase);
}

Using EF1, not EF4

+4  A: 

First works because it is an extension method and is is executing the query as a func, and then filtering your list see here. So in general it would automatically cast the where to

 Where(Func<WorkOrder, bool>

Second doesn't because it is pushing your where statement down to the db. When the lambda expression is evaluated it is expanded like this:

Where( Expresion<Func<WorkOrder, bool>>)

Here is a good article that explains Expressions vs Func

Here is another SO post that helps to explain the difference

[Edit (BlueRaja)]

This new edit appears to be correct. To clarify: it seems Func<WorkOrder, bool> is implicitly castable to Expression<Func<WorkOrder, bool>>, but not the other way around.

There are overloads of Where for both types. .Where(MyCustomMethod) is calling the Func<WorkOrder, bool> one, whereas .Where(o => MyCustomMethod(o)) is calling the Expression<Func<WorkOrder, bool>> one.

Nix
Please check again.. He may be using an anonymous class, but his custom method will still receive a WorkOrder object as a parameter, while your code won't even compile :)
Artiom Chilaru
Perhaps I should have elaborated (see edit above): the code compiles, but it fails at runtime due to *"LINQ to Entities does not recognize the method..."* Example: http://blog.dreamlabsolutions.com/post/2008/11/17/LINQ-Method-cannot-be-translated-into-a-store-expression.aspx This is expected, but the fact that the first one DOES work is unexpected!
BlueRaja - Danny Pflughoeft
@Nix look again :Pnew { WorkOrder = o } is indeed an anonymous class... Inside .Where(o => ... ) o is the anonymous class.. While o.WorkOrder is of type WorkOrder.. in which case the parameter that is passed into his function is of the correct type!
Artiom Chilaru
No comments, dude... no comments...
Artiom Chilaru
It does compile; as I stated, it is a runtime error, occuring only with LINQ-to-Entities, which does not (or isn't supposed to!?) support custom methods
BlueRaja - Danny Pflughoeft
OK, so you are saying that in the first case the overload of `Where` that is called is the one that takes `Func<WorkOrder, bool>`, and in the second case the overload is the one that takes `Expression<Func<WorkOrder, bool>>` -- in the first case the L2E translator recognizes the boundary of the expression tree and knows not to translate anything past that point, and in the second case the expression tree is continued but contains a call to a compiled function, which the translator complains about. That sounds right to me. (SO won't let me change my -1 unless you edit your answer.)
Ben M
Hopefuly BlueRajas edits above helps you.
Nix
This last comment definitely clears up the issue. Thanks, learned something interesting :)
Artiom Chilaru
+1  A: 

Just forming this as an "answer" here, instead of a comment..

I think this is a new feature in .NET 4, where the framework realises that this function cannot be translated to SQL, but can be easily processed in memory. So it gets the whole dataset to the local machine and continues the query processing..

The thing is your first snippet, when translated to an expression tree, would directly say that it runs an external method, while your second snippet is not so "direct". I suppose this is why in the first case L2E can easily understand what's going on, and decide what to do, while in the second case it "thinks" it's better to send an exception and let the developers scratch their heads some more ^_^

Artiom Chilaru
Using EF1, which is .Net 3.5
BlueRaja - Danny Pflughoeft
maybe it's a EF1 vs L2S 1 thing then? Or maybe it's a feature that has been there for some time now?Whatever it is - the best explanation I can come up with is the framework is trying to be smart, based on the expression tree that your query is compiled into :)
Artiom Chilaru