entity framework创建与现有实体关系的新实体,导致尝试创建现有实体的新副本

我正在尝试创建具有特定角色的新用户对象。 “角色”是EF中的现有实体。 我用谷歌搜索了一下,然后堆栈溢出,直到我脸色发青,我已经尝试了所有似乎对其他人都有效的东西。 但是当我尝试保存我的新用户对象时,它首先尝试创建一个新的“角色”,而不是仅仅通过引用现有角色来创建新的用户对象。

我究竟做错了什么?

Role myRole = new Role { ID = myUser.Role.ID }; myObjectContext.Roles.Attach(myRole); myUser.Role = myRole; if (myUser.ID == 0) { myObjectContext.Users.AddObject(myUser); } else { if (myUser.EntityState == System.Data.EntityState.Detached) { myObjectContext.Users.Attach(myUser); } myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified); } myObjectContext.SaveChanges(SaveOptions.None); 

编辑 – 更多测试后……

好吧..所以我无论如何都发现了“原因”的某些部分。 我仍然不知道为什么会这样做并需要帮助。

基本上,我附加到我的新User对象有两组数据。 一个是“角色”,它是包含角色的角色表的FK。 这显示为User上的导航属性,如“User.Role”。

第二组数据是称为“FIPS”的对象集合,它们是用户与另一个名为FIPS的表之间的多对多关系。 它们之间有一个关系表,它只包含两列,每列分别是User和FIPS的外键。 用户的FIPS也是一个引用属性,引用类似“User.FIPS”。

以下是显示在保存上下文之前将FIPS和角色分配给User对象的完整代码。

 List fipsList = new List(); foreach (FIPS fips in myUser.FIPS) { fipsList.Add(fips.FIPS_Code); } myUser.FIPS.Clear(); foreach (string fipsCode in fipsList) { FIPS myFIPS = new FIPS { FIPS_Code = fipsCode }; myObjectContext.FIPSCodes.Attach(myFIPS); myUser.FIPS.Add(myFIPS); } Role myRole = new Role { ID = myUser.Role.ID }; myObjectContext.Roles.Attach(myRole); myUser.Role = myRole; if (myUser.ID == 0) { myObjectContext.Users.AddObject(myUser); } else { if (myUser.EntityState == System.Data.EntityState.Detached) { myObjectContext.Users.Attach(myUser); } myObjectContext.ObjectStateManager.ChangeObjectState(myUser, System.Data.EntityState.Modified); } myObjectContext.SaveChanges(SaveOptions.None); 

我设置我的手表以检查“myObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)”的状态,以查看何时向其中添加内容。

只要将第一个Related对象添加到User对象,就会将尚未附加到上下文的第二个Related对象添加到EntityState为“Added”的上下文中。

….看看是否有办法避免将相关实体附加到用户实体,直到它们全部附加到上下文之后。

–FOLLOWUP–好的..我改变了代码的顺序,以便在分配给用户实体之前将相关实体附加到上下文..但是一旦分配了第一个相关实体,第二个相关实体在ObjectStateEntries中显示为“已添加”。 所以,然后我将其更改为以下顺序:

  1. 将所有相关实体附加到上下文。
  2. 将用户对象上的现有关系移除到相关实体类型。
  3. 将相关实体分配给用户实体。
  4. 保存用户实体。

而且..现在..它的工作..它的工作原理……! =)

自从我编写下面的代码以来已经有一段时间了,但是我模糊地回想起遇到同样的问题并且它正在发生,因为正在添加的角色当前正被上下文跟踪,因此附加存根具有添加新角色的效果相同的Id。

在下面的代码中,我首先检查ChangeTracker并在跟踪角色时使用现有条目。

 // add roles that are in dto.Roles, but not in resource.Roles // use the change tracker entry, or add a stub role var rolesToAdd = fromDto.Roles.Where(r => !toResource.Roles.Any(role => role.Id == r)).ToList(); var roleEntries = dbContext.ChangeTracker.Entries(); foreach (var id in rolesToAdd) { var role = roleEntries.Where(e => e.Entity.Id == id).Select(e => e.Entity).FirstOrDefault(); if (role == null) { role = new Role { Id = id }; dbContext.Set().Attach(role); } toResource.Roles.Add(role); } 

如果您的Role实体已存在于数据库中,为什么要创建它的新实例?

无论如何,如果要手动将新实例附加到上下文,如果数据库中存在附加实例的ID,则它应该有效。 但在你的情况下,以下几行有点奇怪:

 Role myRole = new Role { ID = myUser.Role.ID }; myObjectContext.Roles.Attach(myRole); myUser.Role = myRole; 

首先创建一个具有来自现有Role实例( myUser.Role )的ID的新角色,然后附加新实例,最后再次影响您的实例。 这里肯定有问题。 如果您的角色已经存在(并且在第一行写入myUser.Role.ID时似乎就是这种情况,那么我假设),为什么要创建新实例。

丢掉这3行。 从数据库中获取您的角色。 然后影响来自数据库的RolemyUser.Role属性。

这就是我在我的情况下做到的。

它是一个类似的情况,其中Item包含ICollection这里没有更新,需要在item添加已存在的attribute

首先,我looped遍历item每个attribute

我不得不先把它从当地拆下来

  context.Set().Local .Where(x => x.Id == attr.Id) .ToList().ForEach(p => context.Entry(p).State = EntityState.Detached); 

然后我附上了。

  context.Set().Attach(attr); 

然后我将数据重新加载到它。

  context.Entry(attr).Reload(); 

尝试使用它而不是前三行(如果用户对象已经知道它的角色的ID并且被丢弃,则根本不需要它们):

 int id = myUser.Role.ID; // Role should be NULL, if the user is actually new... // could it be that you wanted to write myUser.RoleID? Role myRole = myObjectContext.Roles.FirstOrDefault(x => x.ID == id); myUser.Role = myRole;