在IEnumerable<>中更新对象不更新?

时间:2011-09-05 06:08:18

标签: c# linq ienumerable

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

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

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();
    }

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

3 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:2)

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

“C#2010中 Pro LINQ语言集成查询”中的报价:

  

“请注意,即使我们只调用了一次查询,结果也是如此   枚举对于每个枚举都是不同的。这是   进一步证明查询是延期的。如果不是,那么   两个枚举的结果都是一样的。这可能是一个   受益或损害。如果您不希望发生这种情况,请使用其中一个   不返回IEnumerable的转换运算符   查询不是延迟的,例如ToArray,ToList,ToDictionary或   ToLookup,用缓存结果创建不同的数据结构   如果数据源发生变化,则不会改变。“

这里有一个很好的解释,有例子:

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

此致

答案 2 :(得分:0)

在@ 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);