1

我有以下 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

4 回答 4

5

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

嗯,有点。必要时对其进行评估。因此,如果您有一百万行,_vaporizerData但其中第一行与过滤器匹配,则它不会遍历其余数据。

当我执行第二个查询(以'tubeA = query'开始)时,我是否正确它会根据第一个查询返回的五行来评估它?

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

_vaporizerData此外,如果在调用 toAny()和调用 to之间有任何其他更改的内容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;
}

编辑:评论中指出的两个要点:

  • 如果我们第一个查询匹配一个项目但第二个不匹配,您的原始查询将引发异常;上面的代码最终会result为 null。这对你来说可能是也可能不是问题。
  • 如果您使用MoreLINQ,您可以通过使用.MaxBy(row => row.CoaterTime)而不是使用OrderByDescendingand来提高效率FirstFirstIIRC,虽然输入为空会失败,所以只有在使用原始版本而不是上面的版本时才应该这样做。
于 2012-08-30T17:19:04.083 回答
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()使事情变得更快——因为我们重用查询结果,而不仅仅是重用查询,存储中间结果的好处可能超过创建列表的成本。

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

于 2012-08-30T17:25:27.080 回答
0

第二个查询不使用第一个查询的结果。所以你用两个查询来命中对象。1 代表.Any()和 1 代表.First()

如果您想重用第一个查询的结果而不再次点击该对象,请ToList()像这样使用:

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

这会将第一个查询结果存储在 中queryquery否则不保存任何值,但实际上是进行查询的语句。

于 2012-08-30T17:18:36.083 回答
0

如果您的基本集合包含的对象多于查询返回的对象,则您可能不应该调用Any,因为正如您所怀疑的那样,它很可能会导致这些对象中的许多对象根据第一组标准被评估两次。ToList相反,通过调用or执行第一个查询ToArray

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; 
} 
于 2012-08-30T17:24:48.193 回答