C#方法返回lambda中的变量

时间:2015-10-12 07:40:35

标签: c# variables methods lambda

所以我正在与一些LINQ(实体框架)lambda进行斗争,我最终通过将结果存储在变量中来解决这个问题。类似的东西:

IQueryable<object> DoStuff() { return /* some linq query */; }

void Main()
{
    var result = DoStuff();
    // (_database is DbContext) == true

    // Works fine
    _database.Table.Where(x => result.Contains(x.Id));

    // Throws "LINQ to Entities does not recognize the method '[...] DoStuff()' method, and this method cannot be translated into a store expression."
    _database.Table.Where(x => DoStuff().Contains(x.Id));
}

我能够通过将查询存储在单独的变量中来解决我的问题(偶然),但我很好奇为什么有效?这是延迟执行的事吗?在此之前,我一直认为两个可交换的方法(方法与中间变量用法)用于引用类型,但显然它们不是。

2 个答案:

答案 0 :(得分:9)

在LINQ to SQL,EF等中,Where()方法中的lambda在执行之前会转换为SQL查询。如果使用自己的方法,则此转换将失败,因为SQL中不知道方法DoStuff()。如果你在LINQ lambda“外部”得到结果,那么SQL查询是用静态值创建的,因此有效......

答案 1 :(得分:6)

当然 - 这是了解lambda表达式会发生什么的问题。

对于LINQ to SQL,EF等,它被转换为expression tree - 基本上代表lambda表达式中代码的数据。然后,LINQ提供程序必须将该数据转换为SQL。如何将DoStuff()的调用转换为SQL?你不能,因为DoStuff()在数据库中不存在,并且提供者不知道该方法在SQL本身中尝试模拟它的方法。 工作的非常有限的一组方法调用实际上是硬编码到LINQ提供程序中,以及转换为SQL。

理论中,一个特定的智能LINQ提供程序可以发现对DoQuery的调用,分析其中的IL以确定它的作用,并检查它是否理解其中的所有内容,并将其转换为SQL。但是,我并不知道任何能够做到这一点的LINQ提供商,并且我很惊讶地看到一个能够可靠地完成它的人,包括DoStuff内的进一步方法调用。这将是一项庞大的工作量,你无论如何都不希望它在执行时发生。 (当然,将它视为概念证明真是太棒了......)

现在,即使使用LINQ to Objects,您的两段代码之间也会有不同的行为。在工作代码中,DoStuff()执行一次,然后结果用于与序列中的每个元素进行比较。在失败的代码中,您需要在序列中按每个元素调用DoStuff()。它仍然可以工作,但你可能会不必要地多次执行查询。请注意,此处lambda表达式的转换是不同的 - 编译器将使用委托转换而不是表达式树转换。

在您的特定情况下,可能会有更多事情发生 - 因为您的方法返回IQueryable<object>,很可能从该方法返回的内容甚至没有作为查询执行 - 但假设它是针对具有相同提供程序的同一数据库的LINQ查询,LINQ提供程序将&#34;理解&#34; DoStuff()返回的查询,并计算出一个SQL查询,包括DoStufF()中的查询部分和Main中的查询部分。查看正在执行的SQL的日志以验证这一点。