LINQ表达式如何知道Where()出现在Select()之前?

我正在尝试创建一个LINQ提供程序。 我正在使用指南LINQ:构建一个IQueryable提供程序系列 ,我已经将代码添加到LINQ:构建一个IQueryable提供程序 – 第四部分。

我正在感受它是如何工作的以及它背后的想法。 现在我遇到了一个问题,这不是代码问题,而是关于理解的问题。

我发了这个声明:

QueryProvider provider = new DbQueryProvider(); Query customers = new Query(provider); int i = 3; var newLinqCustomer = customers.Select(c => new { c.Id, c.Name}).Where(p => p.Id == 2 | p.Id == i).ToList(); 

不知何故,代码或表达式知道在Select之前的Where 。 但是如何以及在哪里?

在代码中没有办法对表达式进行排序,实际上调试模式下的ToString()表明Select位于Where之前。

我试图让代码失败。 正常我先做了Where ,然后是Select

那么表达式如何排序呢? 我没有对指南中的代码进行任何更改。

表达式按照你编写的顺序“解释”,“翻译”或“执行” – 所以WhereSelect之前不会出现


如果你执行:

  var newLinqCustomer = customers.Select(c => new { c.Id, c.Name}) .Where(p => p.Id == 2 | p.Id == i).ToList(); 

然后在匿名类型的IEnumerableIQueryable上执行Where


如果你执行:

  var newLinqCustomer = customers.Where(p => p.Id == 2 | p.Id == i) .Select(c => new { c.Id, c.Name}).ToList(); 

然后在客户类型的IEnumerableIQueryable上执行Where


我唯一能想到的是,你可能会看到一些生成的SQL,其中SELECT和WHERE已被重新排序? 在这种情况下,我猜想在(例如) LINQ to SQL提供程序中的某个地方有一个优化步骤,它采用SELECT Id, Name FROM (SELECT Id, Name FROM Customer WHERE Id=2 || Id=@i)并转换它SELECT Id, Name FROM Customer WHERE Id=2 || Id=@i SELECT Id, Name FROM Customer WHERE Id=2 || Id=@i – 但这必须是提供者特定的优化。

不,在一般情况下(例如LINQ to Objects ),select将在where语句之前执行。 想想它是一个管道,你的第一步是转型,第二步是过滤。 不是相反,如果你写了Where … Select就是这种情况。

现在,LINQ提供程序可以自由地遍历表达式树并在其认为合适时对其进行优化。 请注意,您可能不会更改表达式的语义。 这意味着智能LINQ to SQL提供程序会尝试将尽可能多的where子句引入SQL查询,以减少通过网络传输的数据量。 但是,请记住Stuart的例子 :并非所有查询提供者都很聪明,部分原因是排除查询重新排序的副作用并不像看起来那么容易。