Linq to Objects - 查询执行多少次?

时间:2012-08-30 17:11:03

标签: c# linq

我有以下Linq to Objects查询:

var query = this._vaporizerData
    .Where(row => row.Coater == coater)
    .Where(row => row.Distributor == distributor)
    .Where(row => row.PowerTubeA.HasValue);

if (query.Any())
{
    tubeA = query
        .Where(row => row.CoaterTime.Value >= readTime.AddMinutes(-5))
        .Where(row => row.CoaterTime.Value <= readTime)
        .OrderByDescending(row => row.CoaterTime)
        .Select(row => row.PowerTubeA)
        .First()
        .Value;
}

我知道当执行query.Any()行时,会评估第一个Linq查询。我的问题是这个。假设第一个查询的结果是5行数据。当我执行第二个查询(以'tubeA = query'开头)时,我是否更正它会仅针对第一个查询返回的五行进行评估?

非常感谢。

4 个答案:

答案 0 :(得分:5)

  

我知道当执行query.Any()行时,会评估第一个Linq查询。

好吧,有点儿。它根据需要进行评估。因此,如果_vaporizerData中有一百万行,但第一行与过滤器匹配,则不会迭代其余数据。

  

当我执行第二个查询(以'tubeA = query'开头)时,我是否更正它会仅针对第一个查询返回的五行进行评估?

是的,但是必须再次找到这些行,针对第一次不匹配的所有行检查过滤器。同样,如果_vaporizerData中有一百万行,但这次只有 last 行与原始查询匹配,您最终将再次检查所有999,999个不匹配的行。

此外,如果其他任何内容更改了_vaporizerData之间的内容与Any()之间的调用,那么的更改将会显示为它确实会回到原始数据源。

最好只执行一次查询,例如

First()

编辑:评论中提到了两个重点:

  • 如果第一个查询与某个项匹配但第二个查询没有匹配,则原始查询会抛出异常;上面的代码最终只会以var minTime = readTime.AddMinutes(-5); // Avoid recalculation var result = this._vaporizerData .Where(row => row.Coater == coater) .Where(row => row.Distributor == distributor) .Where(row => row.PowerTubeA.HasValue); .Where(row => row.CoaterTime.Value >= minTime) .Where(row => row.CoaterTime.Value <= readTime) .OrderByDescending(row => row.CoaterTime) .Select(row => row.PowerTubeA) .FirstOrDefault(); if (result != null) { tubeA = result.Value; } 为空。这对您来说可能是也可能不是问题。
  • 如果您使用MoreLINQ,则可以使用result而非使用.MaxBy(row => row.CoaterTime)OrderByDescending来提高效率。 IIRC,虽然在空输入时会失败,所以只有当你使用First的原始版本而不是上面的版本时才应该这样做。

答案 1 :(得分:1)

让我们想象一下这个集合是一个列表。让我们想象它们是否符合前三个标准是:

F, T, F, F, F, T, F, F, F, T, F, F, T, F, T, F, F, F

(T =匹配,F =不匹配)

Any的调用将检查两个元素。那时它知道至少有一个项目。它不会检查更多元素,但返回true。

让我们说那些与其他谓词相匹配的是:

F, F, F, F, F, F, F, F, F, F, F, F, T, F, T, F, F, F

因为我们有一个OrderByDescending,所以必须检查每一个项目,以产生它的结果。没有其他方法可以知道哪个是第一个。

如果没有,它将检查13,将其视为“第一”,并完成。

一方面,这种一般情况可以是ToList()可以使事情变得更快的情况 - 因为我们重用查询结果,而不仅仅是重用查询,存储中间结果的好处可能超过成本创建列表。

另一方面,查询的其他重用正好需要这种行为。

答案 2 :(得分:0)

第二个查询不使用第一个查询的结果。 所以你用两个查询来击中对象。 .Any()为1,.First()

为1

如果您希望重复使用第一个查询的结果而不再次点击该对象,请使用ToList(),如下所示:

var query = this._vaporizerData
    .Where(row => row.Coater == coater)
    .Where(row => row.Distributor == distributor)
    .Where(row => row.PowerTubeA.HasValue).ToList();

这会将第一个查询结果存储在query中,其他明智的query不会保存任何值,但实际上是一个进行查询的语句。

答案 3 :(得分:0)

如果你的基本集合有更多的对象而不是你的查询将返回的对象,你可能不应该调用Any,因为,正如你所怀疑的那样,它很可能导致许多这些对象被评估两次第一套标准。而是通过调用ToListToArray

执行第一次查询
var interimResults = this._vaporizerData 
    .Where(row => row.Coater == coater) 
    .Where(row => row.Distributor == distributor) 
    .Where(row => row.PowerTubeA.HasValue)
    .ToList(); 

if (interimResults.Count > 0) 
{ 
    tubeA = interimResults
        .Where(row => row.CoaterTime.Value >= readTime.AddMinutes(-5)) 
        .Where(row => row.CoaterTime.Value <= readTime) 
        .OrderByDescending(row => row.CoaterTime) 
        .Select(row => row.PowerTubeA) 
        .First() 
        .Value; 
}