【问题标题】:Why does EF throw "NotSupportedException: The method 'First' can only be used as a final query operation"为什么EF抛出“NotSupportedException:方法'First'只能用作最终查询操作”
【发布时间】:2025-12-11 16:55:02
【问题描述】:
int[] ids1 = { 1, 2, 3 };
int[] ids2 = { 1, 5, 6 };

var result = from a in ids1
             where a == ids2.First()
             select a;  
foreach (var item in result) ; //ok


var employees = from c in context.Employees.
                where c.EmployeeID == ids1.First() 
                select c;   
foreach (var item in employees); // NotSupportedException

尝试在 Linq-to-Entities 查询中调用 ids1.First 时,出现异常 System.NotSupportedException:方法“First”只能用作最终查询操作。考虑在此实例中使用“FirstOrDefault”方法

a) 我不明白为什么 First 只能用作最终查询操作,因为在我们的示例中 First 是在 IEnumerable<> ( ids1.First() ) 上而不是在 IQueryable<> 上调用的。换句话说,First 在 Linq-to-Objects 查询中调用,而不是在 Linq-to-Entities 查询中调用?!

b) 无论如何,为什么必须将First 用作最终查询操作,而FirstOrDefault 不必用作最终查询操作?

谢谢


回复:

至于 First() 和 FirstOrDefault() 之间的区别 - 我不知道 知道。你试过了吗,它有效吗?

是的,它有效

否,在 LINQ to Entities 查询中调用了 First()。你在哪里 子句将转换为: Where(c => c.EmployeeID == ids1.First())

a) 我现在有点困惑。我意识到 ids1.First 本质上是在 Linq-to-Entities 查询中调用的,但事实仍然是 FirstIEnumerable<> 上被调用,因此 First 在 Linq-to-Objects 查询中被调用,而这个 Linq -to-Object 查询又在 Linq-To-entities 查询中调用——至少我是这么理解的?!

或者您是在暗示 First 以某种方式在 IQeryable<> 上被调用?

b) 我知道(c => c.EmployeeID == ids1.First()) 会被转换成一个表达式树,但是为什么ids1.First() 没有在转换发生之前执行呢?

c) 无论如何,一旦转换为表达式树,我假设当 sql 提供程序接收到我们的where 子句的表达式树并尝试将其转换为 Sql 命令时,这个 Sql 提供程序没有“权力" 执行ids1.First 以取回结果(然后将其放入 Sql 查询中),因此出现异常?!


第二次回复:

a) 我仍然很困惑为什么在转换为表达式树之前没有执行ids1.First?!即,带有以下子句

Where(c => c.EmployeeID == 2+3) 

表达式2+3 在此Where 子句转换为表达式树之前被执行!而ids.First 也是一种表达方式,所以我会期待类似的行为?!

b) 抱歉重复了,但我的假设是否真的让我很烦 First 在 Linq-to-Objects 查询中被调用,而这个 Linq-to-Object 查询又在 Linq-To-entities 查询中被调用——对吗?!

c) 也许我误解了您的帖子,但您是否暗示大多数其他 Linq-to-Object 运算符可以在 IEnumerable<> E 上调用,即使 E 包含在 Linq-to-Entities 查询中?

【问题讨论】:

    标签: linq-to-entities linq-to-objects


    【解决方案1】:

    不,First() 正在 在 LINQ to Entities 查询中被调用。您的where 子句将转换为:

    Where(c => c.EmployeeID == ids1.First())
    

    该 lambda 表达式将被转换为表达式树。

    当然,在查询之外执行此操作非常简单:

    int firstId = ids1.First();
    var employees = from c in context.Employees
                    where c.EmployeeID == firstId
                    select c;
    

    这变得更简单了:

    int firstId = ids1.First();
    var employees = context.Employees.Where(c => c.EmployeeID == firstId);
    

    至于First()FirstOrDefault() 之间的区别——我不知道。你试过了吗,它有效吗?也许是因为First() 在空序列上调用时会抛出异常,并且由于某种原因,这种行为可能难以翻译。

    编辑:是的,查询提供程序可以可能会查看表达式树的那部分并解决它 - 但迟早你必须划清查询提供程序的智能程度是。它已经做了很多工作,you 很容易在这里完成工作(根据我上面的示例) - 那么为什么不这样做呢?

    请记住,从逻辑上讲,First() 是针对 context.Employees 的每个元素执行的 - 因此它需要在存在任何行的空集合上抛出异常 - 否则 @ 987654331@ 电话从不符合逻辑。看,它并不像您想象的那么简单非常 :) 在这种情况下,您碰巧知道有 元素,因此您可以事先致电First() 而不受惩罚 -但查询提供者不能。

    编辑:回复第二次编辑...

    a) 表达式 2+3 是编译时常量。将其更改为x + 2,加法运算成为表达式树的一部分。特别是,如果您更改 x(或您的示例中的 ids1)的值,这将更改查询 - 您无法更改 2+3 的含义。

    b) 我不清楚你的意思是什么。 EF 查询中包含的表达式树包括对Enumerable.First 的调用,如果您是这个意思。

    c) 由查询提供者决定要支持哪些表达式树 - 这适用于其他方法调用(例如 int.Parse)以及 LINQ to Objects 方法。

    【讨论】:

    • 我很抱歉一直拖着这个话题(我会尽力快速结束它),但你能看到我的第二次编辑
    • @user702769:好的,我也再次编辑了我的答案。不过,我不清楚您真正想要实现什么 - 只是更好地了解正在发生的事情? (当然,这本身就很好。)
    • “我不清楚你真正想要达到的目标是什么——只是更好地理解正在发生的事情?”主要是更好的理解。虽然我感觉如果我不能更好地理解引擎盖下发生的事情,我会在编写查询时遇到一些麻烦。不管怎样,谢谢你的帮助
    • @user702769:不幸的是,对于进程外 LINQ 提供程序,总会有一定数量的“试一试”——您也应该始终检查生成的 SQL。基本上它很棒,但不是灵丹妙药:)