3

Consider the following Person entity:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Using the following Expression (constructed using PredicateBuilder) as the criteria:

var byName = PredicateBuilder.True<Person>().And(x => x.FirstName == "Chaim");

When invoked using the following syntax, the generated SQL is fine (includes the WHERE statement):

ctx.Set<Person>().AsExpandable().Where(x => byName.Invoke(x));

However, when invoked using this slightly different syntax, no SQL WHERE is involved and the filtering is being done by Enumerable.Where instead:

ctx.Set<Person>().AsExpandable().Where(byName.Invoke);

Any thoughts?

haim770
  • 48,394
  • 7
  • 105
  • 133
  • This is *extremely* dangerous. I called `Where(expression.Compile())` instead of `Where(x => expression.Invoke(x))`. Then later, upon tracing our production database for suck, I see `select [every column] from Table`; no where clause. The former comes right from the docs! – dudeNumber4 Feb 14 '19 at 16:45

1 Answers1

4

There is no implicit conversion from a method group to an Expression (of a corresponding delegate type). There is an implicit conversion from a method group to a delegate of a matching signature. Therefore only the IEnumerable overload matches.

Of course, that's not to say that you need to use a lambda. Just write:

ctx.Set<Person>().AsExpandable().Where(ByName);

Since you're passing in an expression (ByName is, after all, an Expression<Person, bool> already, which is exactly what Queryable.Where<Person> requires) this will evaluate as a query, not in linq to objects.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Interesting. so you're basically saying that `ByName.Invoke` is `Func` then only matches `IEnumerable.Where`, while `x => ByName.Invoke(x)` is `Expression>` then matches `IQueryable.Where`? – haim770 Nov 27 '13 at 20:14
  • 1
    @haim770 `x => ByName.Invoke(x)`, being a lambda, can match *either*. It can be an expression, or a delegate. Because of that, both `Queryable.Where` and `Enumerable.Where` are valid options, and it moves on to a betterness algorithm. `Queryable` wins. `ByName.Invoke`, being just a method group, like any other method group, is not convertible to an `Expression`, so `Enumerable.Where` is the only possible valid overload. – Servy Nov 27 '13 at 20:18
  • I see. choosing the 'better algorithm' is performed at runtime? how? – haim770 Nov 27 '13 at 20:20
  • @haim770 It's done at compile time. See the specs on overload resolution for the details. – Servy Nov 27 '13 at 20:21