9

This works:

Entities.WorkOrderSet.Where(MyCustomMethod);

This does not:

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

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • What exception do you get when you try to run the second one? – Artiom Chilaru Apr 20 '10 at 14:19
  • Also.. are you sure EF doesn't just get the whole table and run the function locally when you run your first shippet? Run the SQL profiler, and check what query is sent to the server? – Artiom Chilaru Apr 20 '10 at 14:20
  • I get "LINQ-To-Entities cannot recognize the method..." - this is the expected behaviour, since custom methods can't be translated to SQL. The usual fix is to call .ToList() first... but for some reason, it appears to be working without that! – BlueRaja - Danny Pflughoeft Apr 20 '10 at 14:23
  • @Artiom: As expected, `MyCustomMethod` is not being translated to SQL. The question still exists, however: why does .ToList() not have to be called first in the first example, but it does in the second? – BlueRaja - Danny Pflughoeft Apr 20 '10 at 14:28

2 Answers2

6

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.

Community
  • 1
  • 1
Nix
  • 57,072
  • 29
  • 149
  • 198
  • 1
    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 Apr 20 '10 at 14:18
  • 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 Apr 20 '10 at 14:22
  • @Nix look again :P new { 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 Apr 20 '10 at 15:00
  • 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 Apr 20 '10 at 15:36
  • 1
    OK, so you are saying that in the first case the overload of `Where` that is called is the one that takes `Func`, and in the second case the overload is the one that takes `Expression>` -- 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 Apr 20 '10 at 16:03
  • This last comment definitely clears up the issue. Thanks, learned something interesting :) – Artiom Chilaru Apr 20 '10 at 18:11
1

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
  • 11,811
  • 4
  • 41
  • 52
  • 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 Apr 20 '10 at 14:44