当将lambda作为参数传递给LINQ时,'LINQ to Entities不支持LINQ表达式节点类型'Invoke',但直接使用时不支持

时间:2018-10-14 14:19:04

标签: c# entity-framework linq lambda

在有人将其作为thisthis和许多其他类似问题的副本进行投票之前,请仔细阅读该问题,因为我认为不是(即使看起来非常相似) )。

我有一个Linq查询,如下所示...

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

......,其中pitIDint,而valsList<int>

这很好,但是由于我有四个这样的查询,只是Where子句中的lambda有所不同,我认为最好将代码提取到一个通用方法中……

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

然后我可以这样称呼...

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

但是,此方法给出了运行时异常“ System.NotSupportedException:'LINQ to Entities不支持LINQ表达式节点类型'Invoke'。'

在读取了其他问题的例外情况后,我设法通过如下更改方法来解决它...

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

但是,我似乎找不到的一件事是为什么我上面的第一个查询(带有Where子句中的lambda)不起作用,而第二个查询是提取方法呢?我认为这与实际的lambda无关,因为它不包含EF无法转换为SQL的任何内容(这在第一个版本中确实如此),因此显然与以下事实有关: lambda作为Func而不是Expression传递。

我找到的最接近解释的是对this question的回答,但这是基于以下事实:传入的代码无法转换为SQL。正如我所说,我认为这不是问题所在,因为EF设法在第一个代码段中很好地将其翻译了。

任何人都能解释为什么我的示例不适用于Func吗?

作为次要问题,任何人都可以解释为什么.Where(a => p(a))p上给出了“ 期望的方法名称”的编译器错误,但对于{{1} }?我认为这些是等效的。

谢谢

2 个答案:

答案 0 :(得分:3)

一个Expression对象被编译成一个数据结构(一个表达式树)。 EF在运行时将其转换为SQL代码。另一方面,Func由编译器转换为可执行的IL代码。当您要求EF转换包含Where(x => f(x))的查询时,您将得到一个Func f,它是IL代码,并且周围有一个小的表达式树,描述了对由f表示的函数的调用。错误消息的意思是该“调用”表达式树无法转换为SQL,这是合理的,因为要调用的是一段IL代码。

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

答案 1 :(得分:1)

如果您通过varbinary,则可能有效。 Experssion<Func<MyType, bool>>是对.net编译方法的引用。 Func是一个表达式树,具有与该函数相同的调用签名。