为什么EF说“具有相同键”的对象在“已分离”时已经存在?

时间:2013-02-19 12:03:28

标签: c# entity-framework ef-code-first repository-pattern

在我的存储库更新方法中,我首先附加可能需要在执行任何其他操作之前添加到更新实体的集合中的现有对象,以便EF意识到我想将该实体与另一个现有实体相关联,而不是创建一个新的一个。所以在这个例子中,我是用用户组来做的:

UserGroup basicGroup = repoGroup.GetGroupByName("BasicGroup");
List<UserGroup> updatedGroups = new List<UserGroup> { basicGroup };
foreach (var user in usersToUpdate) {
    repoUser.UpdateUser(user.userId, updatedGroups);
}

[...]

public bool UpdateUser(int updatedUserId, List<UserGroup> updatedGroups) {
    using (var context = new MyDbContext()) {
        if (updatedGroups != null) {
            // Attach groups to context before add so that EF creates a new
            // relationship between entity and new items in groups collection,
            // instead of creating a new entity for each new item.
            foreach (UserGroup group in updatedGroups) {

                var testEntry = context.Entry(group);

                context.UserGroups.Attach(group);
            }
        }

        var userToUpdate = context.Users.First(usr => usr.id == updatedUserId);
        userToUpdate.UserGroups.Clear();
        foreach (var group in updatedGroups) {
            userToUpdate.UserGroups.Add(group);
        }

        context.SaveChanges();
        return true;
    }
}

所以我的代码会循环遍历多个用户,并尝试将一组新的组与它们相关联。我意识到我在这里犯的错误是我的.Attach可能试图将同一个实体附加到对象上下文两次 - 在这种情况下,basicGroup。所以我认为我可以测试是否已经附加了对象,如果没有附加它,则只附加它。

所以看一下testEntry行。在那里,我尝试获取group实体的Entry状态。我确实得到了一个DbEntityEntry实例,但总是说EntityStateDetached(在每次循环迭代中)。现在,当我运行我的代码时,在foreach循环的第一次迭代中就可以了,但在第二次迭代中,我得到了异常:

  

ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。

我不明白这一点;如果对象已存在于ObjectStateManager中,为什么我的DbEntityEntry表示其状态为Detached?当然应该记住它的状态是Unchanged

1 个答案:

答案 0 :(得分:0)

我不久前就遇到过这个问题了,基本上是编译器告诉你,在你的上下文中,你有一个实体加载到内存中,然后你试图附加回相同的上下文(第二个forloop迭代 - 附加的对象在初始上下文中。)

要解决此问题,对于.Add方法,请使用单独的上下文(新的&#34;使用&#34;调用),这将起作用。这是我能找到的唯一解决方法。

即。 (我的UnitOfWork =你的正常背景)

UnitOfWork _context = new UnitOfWork();

// Add 1
_context.Add(obj);

using(UnitOfWork context = new UnitOfWork()){
   // Do second addition routines here based on objects used in add 1.
   context.Add(childobj);
   context.SaveChanges();
}

_context.SaveChanges();