1

我很难在实体框架中更新实体。

场景: - 我使用 new DbContext().GetById(guid) 加载实体 - 我尝试使用扩展方法保存该实体,然后使用 new DbContext()

这是我的更新方法:

 public virtual void Update(IEntity entityToUpdate)
    {
        var dbEntry = Context.Entry(entityToUpdate);
        if (dbEntry == null) return;

        if (Context.Entry(entityToUpdate).State == EntityState.Detached)
            DbSet.Attach(entityToUpdate);
        else
        {
            dbEntry.CurrentValues.SetValues(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        Context.SaveChanges();
    }

这是我的尝试集合。如果我使用 SetValues,我会被告知实体已分离,因此不可能,如果我使用附加,则会收到以下错误:'ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。

我显然在做一些根本错误的事情。有人可以在正确的方向上帮助我吗?

更新:

    protected void TransferClubs(object sender, EventArgs e)
    {
        var clubHelper = new ClubHelper();
        var club = clubHelper.GetClub(new Guid("A009D0CD-71C4-42E8-88E2-037F059B12EE"));
        club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);
        club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);

        club.Save();
    }

    public static bool Save(this ClubItem item)
    {
        var clubHelper = new ClubHelper();
        clubHelper.AddOrUpdate(item);
        return true;
    }

    public ClubItem AddOrUpdate(ClubItem item)
    {
        if (item.Id == Guid.Empty)
            Insert(item);
        else
            Update(item);

        return item;
    }

还有你在我原来的帖子中看到的 Update() 方法......

4

1 回答 1

2

在我看来,如果您在TransferClubs最后一行替换club.Save();

clubHelper.SaveChanges();

该方法应该简单地调用Context.SaveChanges();并保存对加载实体的更改,即创建两个新用户,并将外键设置为加载的club.

老实说,您更新实体的方法相当奇怪和复杂。我不知道为什么您的代码会引发“ ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象”异常。但是有几个缺陷和没有意义的事情:

  • 您没有处理实例化的上下文。如果ClubHelper创建了一个新的上下文,它应该在超出范围时处理它。因此,ClubHelper应该实施IDisposable并且实施Dispose应该调用context.Dispose(). 然后您可以使用using在最后自动处理实例化对象的 a:

    protected void TransferClubs(object sender, EventArgs e)
    {
        using (var clubHelper = new ClubHelper())
        {
            // stuff...
        } // Dispose called here automatically
    }
    
  • 这些行没有意义:

    var dbEntry = Context.Entry(entityToUpdate);
    if (dbEntry == null) return;
    

    If entityToUpdateis an entity of your object model dbEntryis never . 如果是您的对象模型的实体,则永远不会null。它只能有 state Detached。If entityToUpdateis not an entity of your object modelEntry抛出异常但不返回null

  • 这个if案子没有意义:

    if (Context.Entry(entityToUpdate).State == EntityState.Detached)
        DbSet.Attach(entityToUpdate);
    // ...
    Context.SaveChanges();
    

    Attach在 state 中将实体添加到上下文中Unchanged。如果您只是SaveChanges在此之后调用,则不会发生任何事情并将被写入数据库,因为没有任何变化。

  • 这个else案子也没有意义:

    dbEntry.CurrentValues.SetValues(entityToUpdate);
    Context.Entry(entityToUpdate).State = EntityState.Modified;
    

    SetValues如果属性具有相同的名称,则将 的属性复制entityToUpdate到具有相同键且已附加到上下文的实体的属性。如果属性值不同,则将属性标记为Modified。如果您之后将整个实体设置为Modified您将所有属性标记为Modified之前SetValues冗余。

    此外,这两行都不会帮助您创建所需的 UPDATE 语句,因为它们不会将关系标记为已修改,并且仅影响标量(和复杂)属性,而不影响导航属性。但是更新关系 - 即club两个新用户之间的关系 - 正是您在示例中所需要的。

  • 最后,在全新的上下文中执行更新是没有意义的......

    var clubHelper = new ClubHelper();
    clubHelper.AddOrUpdate(item);
    

    ...当您在另一个上下文中仅在之前的行中加载和更改实体时。Entity Framework 已经完成了所有工作来跟踪您在第一个上下文中的更改以生成所需的 SQL 语句。如果您在此之后创建一个新的上下文,您希望在其中保存更改,则您放弃所有工作并且必须从头开始告诉 EF 您对原始实体做了哪些更改。

于 2012-06-14T14:10:03.933 回答