【问题标题】:Slow foreach() on a LINQ query - ToList() boosts performance immensely - why is this?LINQ 查询上的慢 foreach() - ToList() 极大地提高了性能 - 为什么会这样?
【发布时间】:2010-08-26 14:05:52
【问题描述】:

我有点理解整个延迟执行的概念,但以下让我感到困惑......

在包含大约 1000 行的 DataTable 上,我调用 AsEnumerable()。然后,我选择返回到强类型类的 IEnumerable 中的实体 (1)... 这就是我感到困惑的地方:我在集合上执行了一个 foreach 循环;使用一堆 Where() 调用 (2) 从集合中的各个项目中选择东西......而且速度非常慢。

  1. DataTable.AsEnumerable().Select(r => new ObjectRepresentation { ... });
  2. item.Where(i => i.SomeEnum == SomeEnum.Something)


...但是如果我在对 DataTable 调用 AsEnumerable() 之后立即调用 ToList(),则 foreach 循环只需不到一秒的时间即可完成。

我在这里缺少什么?每次循环迭代时,我是否有效地调用 AsEnumerable() ?或者每次我访问集合中的项目时?或者每次我对集合中的一个项目进行 Where() 调用时?还是以上所有?


更新

有点完整的代码:

public class ObjectRepresentation
{
    public SomeEnum SomeEnum { get; set; }
}


var collection = DataTable.AsEnumerable().Select(r => new ObjectRepresentation
{
    SomeEnum = (SomeEnum)Convert.ToInt32(r["SomeEnum"])
});

foreach(var item in collection) // slow loop
{
    // 10 or so Where() calls on item inside this loop
}

collection = collection.ToList(); // Hit hyper speed button!

foreach(var item in collection) // fast loop
{
    // 10 or so Where() calls on item inside this loop
}

【问题讨论】:

  • 听起来您在每次迭代时都在进行数据库调用。您可以运行 SQL Profiler 来查看是否属实...
  • 为什么要调用 AsEnumerable()?如果 AsEnumerable 已实现 IEnumerable,则 AsEnumerable 在编译时将对象更改为 IEnumerable。为什么不使用表的 Rows 属性来迭代行?
  • @Wix: DataTable 尚未实现 IEnumerable<T>。当您在 DataTable 上调用 AsEnumerable 时,您调用的是 DataTableExtensions.AsEnumerable 方法,不是 Enumerable.AsEnumerablemsdn.microsoft.com/en-us/library/…
  • 嗯...您在抱怨 foreach 速度很慢...但您从未真正向我们展示过 foreach。给我们看一些更完整的代码,我们也许可以让它更快……
  • 在你的问题中你说 但是如果我打电话给ToList() 我的AsEnumerable() (这对我来说是一个有趣的问题) ,但是在您的代码中,您在Select 之后调用ToList,而不是AsEnumerable(我认为这完全是关于延迟执行 - 查询一遍又一遍地运行)。但是如果没有在foreach 代码中看到item 上的Where,就很难给出一个确定的答案。你能告诉我们Where是如何在foreach中被调用的吗(特别是因为ObjectRepresentation不是IEnumerable<T>)?很可能您在 collection 变量上调用 Where

标签: c# linq


【解决方案1】:

您不了解哪些方法是延迟的,哪些不是,因此您不了解您的代码何时定义操作与执行操作。

这些都是延期的。它们定义但不执行操作。

source.AsEnumerable
source.Select
source.Where

这些枚举源,因此不会被延迟。

source.ToList
source.First
source.Single
foreach(var x in source)

【讨论】:

    【解决方案2】:

    在您输入之前,它不会从数据库中获取所有项目

     ToList or First or Single
    

    在 foreach 中,您向数据库中发送每个项目的查询。所以它工作得更慢。打开您的 sql profiler 以更好地理解。

    【讨论】:

    • 在 OP 的特定场景中,他可能会导致为每个迭代记录发生数据库查询,但通常使用 foreach 枚举延迟查询将分别查询每个项目。首次使用枚举器时会获取整个集合。
    • 他在查询数据表,而不是 Linq DataContext 或 ObjectContext。对数据表的查询不会导致数据库查询执行
    • 是的,你是对的。但这是理解 linq 对方法的作用的一般解释。对于每一项,获取数据比之前对每条语句使用 ToList 需要一些时间。
    【解决方案3】:

    确实,您似乎不清楚什么是代码执行,以及在实际使用结果时(可能)稍后执行的意图定义是什么。我建议阅读 LINQ 的这一部分。

    并且可能尝试使用附加的调试器执行您的两个变体,这样您就可以实际查看哪些代码以何种顺序执行以及您的数据实际发生了什么。您可能会在这里获得(大?)惊喜......

    【讨论】:

      猜你喜欢
      • 2016-01-20
      • 1970-01-01
      • 1970-01-01
      • 2012-10-03
      • 2010-12-09
      • 1970-01-01
      • 2017-11-23
      • 2017-11-19
      • 2014-07-17
      相关资源
      最近更新 更多