【问题标题】:EF6 - updating child objects in a list of parent object causes enumeration errorEF6 - 更新父对象列表中的子对象会导致枚举错误
【发布时间】:2016-05-12 03:16:52
【问题描述】:

我需要更新一个对象列表,每个对象都有一个子对象列表。当我尝试将子对象添加到数据库上下文时,我收到一条错误消息,提示“集合已修改,枚举操作可能无法执行”。我试过改变 ToList() 但这没有帮助。这是一个示例:

public void Update(List<Parent> parents)
{
    // I've made this a FOR loop instead of FOREACH - doesn't help
    for (int i = 0; i < parents.Count; i++)
    {
        var parent = parents[i];
        var dbEntry = _db.Entry(parent);
        dbEntry.State = EntityState.Modified;

        foreach (var child in dbEntry.Entity.Children)
            if (!parent.Children.Exists(ent => ent.Id == child.Id))
                _db.Children.Remove(child);

        foreach (var child in parent.Children)
            if (child.Id == 0)
                dbEntry.Entity.Children.Add(child); <-- ENUMERATION ERROR HERE
    }
    context.SaveChanges();
}

我到处寻找答案,但找不到这个答案。我尝试修复它的每一种方法最终都会得到相同的结果。有什么想法吗?

【问题讨论】:

  • 您的问题可能与 EF6 无关;它可能与foreach 是一个只读循环有关...您遇到的错误是修改当前由foreach 操作枚举的任何集合的典型错误。你可以使用for 循环代替内部循环吗?
  • @CoolBots 我认为修改您正在迭代的集合实际上可能是一个天生的坏主意。 OP,你提到使用 ToList();你在哪里用的?
  • @Ed Plunkett,我一般同意,只是试图解决可能是 OP 错误的直接原因。

标签: c# entity-framework linq


【解决方案1】:

从外观上看,您正试图将孩子添加到您在最后一次 foreach 中迭代的完全相同的集合中。

var dbEntry = _db.Entry(parent);

所以

parent = dbEntry.Entity

问题出在这个 foreach 上

foreach (var child in parent.Children)
        if (child.Id == 0)
            dbEntry.Entity.Children.Add(child); <--dbEntry.Entity points to same variable as parent. 

在上面foreach

parent.Children == dbEntry.Entity.Children

更改跟踪选项

如果您启用了更改跟踪并且Parent.Children 属性映射正确,则您无需执行任何操作。

如果您禁用了自动更改跟踪,您可能需要做的就是:

    var dbEntry = _db.Entry(parent);
    dbEntry.State = EntityState.Modified;

可能,您可能需要遍历每个孩子并将其标记为已添加,例如:

foreach (var child in parent.Children)
     if (child.Id == 0) // This is a new Child
         _db.Entry(child).State = EntityState.Added

观察:

以下if 声明永远是错误的!

    foreach (var child in dbEntry.Entity.Children)
        if (!parent.Children.Exists(ent => ent.Id == child.Id))

如果您想删除已从列表中删除的孩子,但您知道您确实在没有AsNoTracking() 的情况下请求了他们。然后您应该能够使用_db.Children.Local 属性,因为这将包含您检索到的所有实体。但请确保您检查孩子是否存在于任何List&lt;Parents&gt; 中,否则您可能会尝试删除已更换父母的孩子,例如被采纳了。

你能告诉我你为什么要手动管理这个状态吗?

为什么不让 EF 为您跟踪状态变化?然后你不需要担心除了在其他地方添加/删除实际的孩子,然后只需调用_db.SaveChanges()

【讨论】:

  • 是的,这似乎是正确的,你能建议如何解决这个问题吗?
  • 如果 Michal 没有回答这个问题,我建议你回答 parent.Children.ToList()。顺便说一句,您不妨使用parent.Children.Add(child)
  • @RickWheeler 你确定要在你的实体 Children 集合中加倍孩子吗?如果 Children 集合是映射/导航属性实体,则可能不需要您的最后一个。
  • 您能否详细说明您要达到的目标?之前和之后的小样本对象图会很有用
  • @GertArnold 问题是,为什么他试图从数据库中完全删除孩子(第一次 foreach),然后将它们的 second 副本添加到 List 中已经有它们了。完全没有意义
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-25
  • 2013-02-22
相关资源
最近更新 更多