【问题标题】:Update detached objects in EF that already exist in object state manager更新 EF 中已存在于对象状态管理器中的分离对象
【发布时间】:2014-02-12 14:12:11
【问题描述】:

我希望这将是一个简单的问题,但对于我的生活,我无法在 SO 或任何其他网站的其他地方找到具体答案。

我有一个使用实体框架代码优先的基本存储库/工作单元模式。除了某些更新情况外,这一切都很顺利。问题是我有一组实体框架模型对象,它们都以 EF 返回的“Db”为前缀,但我随后将它们转换为普通的 DataContract 模型对象以传递给 Web 层以实现关注点分离。我有一个基本的转换接口,它只是从 DataModel 对象中填充一个 WebModel 对象,逐个字段逐字复制。

因此,如果您从 EF 检索 ID 为 1 的 DbUser 对象,然后转换为 User 对象,然后将该 BACK 转换为 DbUser 对象,您最终会得到 ID 为 1 的 DbUser,但它是一个不同的对象对于您开始使用的对象,尽管它们具有相同的主键字段,但实际的 CLR 对象本身是不同的。

以下作品

User user;
using (var work = new UnitOfWork())
{
    var repository = new UserDataRepository(work);
    user = repository.Get(1);
    repository.save();
}

var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser); 

using (var work = new UnitOfWork())
{
    var repository = new UserDataRepository(work);
    repository.Update(user);
    repository.save();
}

由于它们使用两个不同的工作单元/上下文,因此第二个块在 ObjectStateManager 中没有可比较的内容,并且可以在 Update() 方法中附加分离的对象

这不起作用

using (var work = new UnitOfWork())
{
    var repository = new UserDataRepository(work);
    user = repository.Get(1);
    repository.save();

    var modelUser = DataConverter.Convert(user);
    modelUser.Name = "new name";
    user = BusinessConverter.Convert(modelUser)

    repository.Update(user);
    repository.save();
}

注意:从逻辑上讲,我知道转换并只是转换回来没有多大意义,但是继续使用它,我已经大大简化了示例以使其更容易写在纸上,在我的实际代码中是有原因的因为那样做。

我收到常见错误“objectstatemanager 中已存在具有相同键的对象...”。我假设因为 Get() 将对象加载到 EF 中,然后更新看到该对象已分离,然后尝试附加它并且它已经存在。

我的存储库中的更新方法如下

public override bool UpdateItem(DbUser item)
        {
            if (Work.Context.Entry(item).State == EntityState.Detached)
                Work.Context.Users.Attach(item);

            Work.Context.Entry(item).State = EntityState.Modified;

            return Work.Context.Entry(item).GetValidationResult().IsValid;
        }

【问题讨论】:

  • 为什么不使用工作方法?
  • 解释起来有点复杂,但基本上我的 Web 服务方法之一需要从数据库中获取“帖子”,更新查看计数以显示新用户查看了此帖子,并将帖子返回到网络层。这涉及同一 UnitOfWork Web 方法中的 Get 和 Update。对于测试框架来说也非常不方便,因为每次我获取或更新存储库时,我都需要销毁并重新创建一个新的工作单元

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


【解决方案1】:

我将这个扩展方法添加到 DbContext 以毫无问题地重新附加实体尝试一下:

public static void ReAttach<T>(this DbContext context, T entity) where T : class
{
    var objContext = ((IObjectContextAdapter) context).ObjectContext;
    var objSet = objContext.CreateObjectSet<T>();
    var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

    Object foundEntity;
    var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
    // Detach it here to prevent side-effects
    if (exists)
    {
        objContext.Detach(foundEntity);
    }
    context.Set<T>().Attach(entity);
}

然后只需更新您的方法:

public override bool UpdateItem(DbUser item)
{
    Work.Context.ReAttach(item);    
    Work.Context.Entry(item).State = EntityState.Modified;    
    return Work.Context.Entry(item).GetValidationResult().IsValid;
}

【讨论】:

  • 非常接近,但现在我遇到了一个问题,因为我没有提到我的 DbUser 对象中有一堆相关的实体,例如 UserAccount、UserSearchCriteria、IList,我是猜测还需要做些什么来分离这些?现在使用您的扩展方法,当它到达重新附加的最后一行时,我收到错误“{”违反多重性约束。关系“MyStory.Data.EF.Contexts.DbUser_Account”的角色“DbUser_Account_Target”的多重性为 1 或 0..1。"}"
  • @user1122909 请提前包含这些重要信息。
  • 我给了这个投票,因为我发现它很有用,虽然,对于那些阅读,我不能让它直接工作。我遇到的问题是,如果entity 没有从数据库中已有的内容修改,您会得到unexpected number of rows modified (0)... 异常(或类似的东西。)但是,此代码可用于检索foundEntity然后改用context.Entry(foundEntity).CurrentValues.SetValues(entity) 之类的东西。
【解决方案2】:

您可能会得到一个托管实体,并再次将新的DbUser 的属性逐字映射到托管对象:

public override bool UpdateItem(DbUser item)
{
  using (var work = new UnitOfWork())
  {
    var repository = new UserDataRepository(work);
    DbUser managedUser = repository.Get(item.PK);

    //foreach DbUser property map the item to managedUser
    managedUser.field1 = item.field1;
    [..]

    repository.Update(managedUser);
    repository.Save();
  }
}

【讨论】:

    【解决方案3】:

    如果您将上下文设置为 AsNoTracking(),这将停止 aspmvc 跟踪对内存中实体的更改(无论如何这都是您在网络上想要的)。

    _dbContext.Products.AsNoTracking().Find(id);
    

    我建议您在 http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application 阅读更多相关信息

    亚什

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-30
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      相关资源
      最近更新 更多