【问题标题】:Update object in IEnumerable<> not updating?IEnumerable<> 中的更新对象不更新?
【发布时间】:2011-09-05 06:08:18
【问题描述】:

我有一个 POCO 类型的 IEnumerable,包含大约 80,000 行 和一个 db 表 (L2E/EF4),其中包含存在“错误/差异”的行子集(大约 5000 行,但经常重复以提供大约 150 个不同的条目)

以下代码获取不同的 VSACode 的“错误”,然后尝试更新完整的结果集,只更新匹配的行...但它不起作用!

var vsaCodes = (from g in db.GLDIFFLs
  select g.VSACode)
  .Distinct();

foreach (var code in vsaCodes)
 {
  var hasDifference = results.Where(r => r.VSACode == code);
  foreach (var diff in hasDifference)
   diff.Difference = true;
 }

 var i = results.Count(r => r.Difference == true);

在这段代码之后,i = 0

我也试过了:

foreach (var code in vsaCodes)
    {
        results.Where(r => r.VSACode == code).Select(r => { r.Difference = true; return r; }).ToList();
    }

如何更新“结果”以仅设置匹配的差异属性?

【问题讨论】:

    标签: c# linq ienumerable


    【解决方案1】:

    假设results 只是一个查询(您还没有显示它),每次迭代它时都会对其进行评估。如果该查询每次都创建新对象,您不会看到更新。如果它返回对相同对象的引用,你会的。

    如果您将 results 更改为 物化 查询结果 - 例如通过将 ToList() 添加到末尾 - 然后迭代 results 不会发出新查询,您会看到您的更改。

    【讨论】:

    • 我(显然不太正确)的理解是避免使用 ToList() 除非你真的需要它......我想这是一个“真的需要它”的情况?快问?在我的语句/查询链中尽早调用 ToList() 以实现列表是否更好?如果我不这样做,它会在每次使用时查询数据库吗?
    • @BlueChippy:您通常应该尽可能晚地使用 ToList,以便大多数查询(特别是过滤输出的部分)在数据库中执行。
    • 谢谢乔恩,我猜我把这个 ToList() 留了一点 TooLate() ;)
    • @JonSkeet:稍微调整一下,如果您对底层“物化”源有查询,您可以通过结果查询更新源对象。每次都会重新评估查询,如果您的查询包含过滤器,您可以根据更新过滤掉项目,但源将保持更新。如果您的查询包含到类(而不是匿名类型)的投影(Select()),这将断开与源对象的链接。
    • @andleer:恐怕我不太清楚你的意思。也许您应该添加自己的答案?
    【解决方案2】:

    前段时间我也遇到过同样的错误。问题是 linq 查询通常会被延迟,并且在您调用它们时不会执行。

    引自“Pro LINQ Language Integrated Query in C# 2010”:

    "请注意,即使我们只调用了一次查询,结果 每个枚举的枚举都不同。这是 进一步证明查询被推迟。如果不是,则 两次枚举的结果是相同的。这可能是一个 好处或坏处。如果您不希望发生这种情况,请使用以下之一 不返回 IEnumerable 的转换运算符,以便 查询不延迟,例如 ToArray、ToList、ToDictionary 或 ToLookup,使用缓存结果创建不同的数据结构 如果数据源发生变化,它不会改变。”

    在这里你有一个很好的解释和例子:

    http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx

    问候

    【讨论】:

      【解决方案3】:

      在@jonskeet 的回答中非常接近地解析单词...

      如果您的查询只是一个过滤器并且基础源对象已更新,则查询将被重新评估,并且可能会根据过滤条件排除这些对象,在这种情况下,您的查询结果将在后续枚举中发生变化,但基础对象仍将已更新。

      就更新和持久化更改的对象而言,关键是缺乏对新类型的投影。

      ToList() 是解决此问题的常用方法,如果有一个新类型的投影,但如果您的查询过滤但没有投影,事情就会变得模糊不清。鉴于所有内容都是 referencing 相同的对象,对查询的更新仍会影响原始源对象。

      同样,解析单词,但这些极端情况可能会让您感到困惑。

      public class Widget
      {
          public string Name { get; set; }
      }
      
      var widgets1 = new[]
      {
          new Widget { Name = "Red", },
          new Widget { Name = "Green", },
          new Widget { Name = "Blue", },
          new Widget { Name = "Black", },
      };
      
      // adding ToList() will result in 'static' query result but
      // updates to the objects will still affect the source objects
      var query1 = widgets1
          .Where(i => i.Name.StartsWith("B"))
          //.ToList()
          ;
      
      foreach (var widget in query1)
      {
          widget.Name = "Yellow";
      }
      
      // produces no output unless you uncomment out the ToList() above
      // query1 is reevaluated and filters out "yellow" which does not start with "B"
      foreach (var name in query1)
          Console.WriteLine(name.Name);
      
      // produces Red, Green, Yellow, Yellow
      // the underlying widgets were updated
      foreach (var name in widgets1)
          Console.WriteLine(name.Name);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-25
        • 2013-02-22
        • 1970-01-01
        • 1970-01-01
        • 2016-03-21
        • 2015-11-10
        相关资源
        最近更新 更多