【问题标题】:Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity实体框架创建与现有实体有关系的新实体,导致尝试创建现有实体的新副本
【发布时间】:2023-03-26 19:31:01
【问题描述】:

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

我做错了什么?

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

编辑 - 更多测试后...

好吧..所以无论如何我已经发现了“原因”的一部分。我仍然不知道它为什么会这样,需要帮助。

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

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

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

List<string> fipsList = new List<string>();
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)”的状态,看看什么时候添加了东西。

一旦第一个相关对象添加到用户对象,第二个尚未附加到上下文的相关对象就会添加到上下文中,其 EntityState 为“已添加”。

.... 看看是否有办法避免将相关实体附加到 User 实体,直到它们全部附加到上下文中。

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

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

而且.. 现在.. 它有效.. omg 它有效...! =)

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    我已经有一段时间没有写下面的代码了,但我隐约记得遇到了同样的问题,因为正在添加的角色当前正在被上下文跟踪,所以附加存根具有添加一个具有相同 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<Role>();
    
    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<Role>().Attach(role);
        }
    
        toResource.Roles.Add(role);
    }
    

    【讨论】:

    • 我正在尝试解决这个问题,看看它是否能解决问题。看来此代码适用于不同版本的 EF,因为我在 VS 2010 IDE 中没有“myObjectContext.ChangeTracker”选项。
    • @AmandaMyer,它适用于 EF 4.2。如果您使用的是 EF 4,则可以尝试 ObjectStateManager 类。
    • 这不是正确的答案,但它让我走上了解决它的道路,所以我接受了这个。
    • @AmandaMyer 我也面临着完全相同的问题。你能告诉我解决办法吗?
    【解决方案2】:

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

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

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

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

    删除这 3 行。 从数据库中获取您的角色。然后将来自数据库的Role 影响到myUser.Role 属性。

    【讨论】:

    • 这样做的原因是因为我创建的用户对象最初是在客户端前端创建为json字符串,然后由webmethod反序列化,然后尝试保存它。由于我不想调用数据库以将角色信息序列化,只是为了将其反序列化回数据库,所以我简单地设置了 User.Role.ID 属性,并认为我会更新对它的引用save 方法中的角色。这就是我想要在上面做的。
    • 我尝试直接从数据库分配它,而不是使用 STUB 和 Attach 方法,但它仍然做同样的事情。
    • 奇怪。你能发布角色和用户实体吗?
    • 你要我怎么做?它们是 EF 模型文件的一部分吗?要截图吗?
    • 是的,EF已经生成的实体。看看你的 User 实体的 Role 属性的定义会很有趣。转到属性的定义(在 Visual Studio 中,单击属性并按 F12),然后复制/粘贴它。可能是导航属性有问题。
    【解决方案3】:

    我就是这样做的。

    Item 包含ICollection&lt;Attribute&gt; 的情况类似。这里没有更新,需要将已经存在的attribute 添加到item

    首先我通过item 中的每个attribute looped

    我必须先把它从本地分离出来

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

    然后我附上了。

          context.Set<Model.Attribute>().Attach(attr);
    

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

          context.Entry(attr).Reload();
    

    【讨论】:

      【解决方案4】:

      尝试使用它而不是前三行(如果用户对象已经知道它的角色 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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-28
        • 2019-05-10
        • 2010-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多