使用Entity Framework 4.3更新实体 - 代码优先

时间:2012-06-14 09:29:05

标签: entity-framework

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

场景:   - 我用新的DbContext()加载一个实体.GetById(guid)   - 我尝试使用扩展方法保存此实体,然后使用新的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()方法......

1 个答案:

答案 0 :(得分:2)

我认为如果您在TransferClubs最后一行club.Save();替换

,则更新应该有效
clubHelper.SaveChanges();

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

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

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

    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;
    

    如果entityToUpdate是对象模型的实体,则dbEntry永远不会null。它只能有状态Detached。如果entityToUpdate不是您的对象模型的实体,则Entry会抛出异常,但不会返回null

  • if案例没有意义:

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

    Attach将一个实体添加到状态Unchanged中的上下文中。如果您之后只是调用SaveChanges没有任何反应,并且将被写入数据库,因为没有任何更改。

  • else案例也没有意义:

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

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

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

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

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

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