3

Before anyone votes to close this as a duplicate of this, this and many other similar questions, please read the question carefully as I don't think it is (even though it looks very similar).

I have a Linq query as follows...

List<int> ids = ctx
  .Where(a => a.PartInformationTypeID == pitID && vals.Contains(a.PartDefinitionID))
  .Select(a => a.SystemID)
  .Distinct()
  .ToList();

...where pitID is an int and vals is a List<int>

This works fine, but as I have four such queries, only differing by the lambda in the Where clause, I thought it would be better to pull the code out into a common method...

private List<int> DoAdvancedSearch(Func<MyType, bool> p)
{
  return ctx
    .Where(a => p(a))
    .Select(a => a.SystemID)
    .Distinct()
    .ToList();
}

I could then call this as follows...

List<int> ids = DoAdvancedSearch(systemIDs,
                 a => a.PartInformationTypeID == pitID && vals.Contains(a.PartDefinitionID))

However, this method gives a run-time exception "System.NotSupportedException: 'The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.'"

Having read loads of other questions with the same exception, I managed to solve it by changing the method as follows...

private List<int> DoAdvancedSearch(Expression<Func<MyType, bool>> p)
{
  return ctx
    .Where(p)
    .Select(a => a.SystemID)
    .Distinct()
    .ToList();
}

However, one thing I can't seem to find is why my first query above (with the lambda in the Where clause) didn't work, wheras the second query in the extracted method did? I don't think it's an issue with the actual lambda, as it doesn't contain anything that EF can't translate into SQL (which it demonstrably does with the first version), so it's evidently something to do with the fact that the lambda was passed as a Func rather than an Expression.

The closest I found to an explanation was in the answer to this question, but that was based around the fact that the code being passed in couldn't be translated into SQL. As I said, I don't think that's the problem here, as EF managed to translate it fine in the first code snippet.

Anyone able to explain why my example doesn't work with a Func?

As a secondary question, anyone able to explain why .Where(a => p(a)) gave a compiler error of "Method name expected" on p, but was fine with .Where(p)? I thought these were equivalent.

Thanks

4

2 回答 2

5

表达式对象被编译成数据结构(表达式树)。EF 在运行时将其转换为 SQL 代码。另一方面,Func 由编译器转换为可执行的 IL 代码。当你要求 EF 翻译一个包含 Where(x => f(x)) 的查询时,你有一个作为 IL 代码的 Func f,以及一个围绕它的小表达式树,描述对 f 表示的函数的调用。错误信息的意思是这个“Invoke”表达式树不能被翻译成 SQL,这是合理的,因为被调用的是一段 IL 代码。

请注意,在 Where 调用是内联的第一个示例中,您使用的是表达式,而不是 Func。这是因为 C# 中的 lambda 表达式可以同时具有这两种类型,并且在 IQueryable 上使用 Where 扩展方法时,参数是 Expression 类型,因此整个 lambda 被编译为表达式树。

于 2018-10-14T14:28:58.710 回答
1

如果你通过Experssion<Func<MyType, bool>>它可能会工作。 Func是对 .net 编译方法的引用。 Expression<Func是与函数具有相同调用签名的表达式树。

于 2018-10-14T14:41:15.007 回答