为什么EF抛出“NotSupportedException:方法'First'只能用作最终查询操作”

时间:2011-10-18 17:51:52

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

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<>。换句话说,在Linq-to-Objects查询中调用First而不是Linq-to-Entities查询?!

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

谢谢


REPLY:

  

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

是的有效

  

不,在LINQ to Entities查询中调用First()。你的位置   子句将转换为:Where(c =&gt; c.EmployeeID == ids1.First())

a)我现在有点困惑。我意识到ids1.First基本上是在Linq-to-Entities查询中调用的,但事实仍然是在First上调用IEnumerable<>,因此在Linq-中调用First 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)抱歉重复,但是我的假设 - 那真的让我烦恼 在Linq-to-Objects查询中调用First,然后在Linq-To-entities查询中调用此Linq-to-Object查询 - 是否正确?!

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

1 个答案:

答案 0 :(得分:8)

不,在LINQ to Entities查询中调用First() 。您的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()在调用空序列时会抛出异常,并且由于某种原因,这种行为可能很难翻译。

编辑:是的,查询提供程序可以潜在地查看表达式树的那一部分并将其解决 - 但迟早你必须在查询提供程序有多聪明的情况下绘制一条线。是。它已经做了很多工作,很容易在这里完成工作(按照上面的例子) - 所以为什么不这样做呢?

请记住,逻辑上,First()的每个元素都会执行context.Employees - 所以如果存在任何行,则需要在空集合上抛出异常 - 否则First()调用从不在逻辑上进行。看,它不是完全就像你想象的那样简单:)在这种情况下你碰巧知道元素,所以你可以不受惩罚地调用First()事先 - 但查询提供者不能。

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

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

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

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