【问题标题】:Update entity graph Ids with Db generated Ids in Disconnected environment(EF)在断开连接的环境(EF)中使用 Db 生成的 Id 更新实体图 Id
【发布时间】:2014-08-06 07:45:22
【问题描述】:

我在 WinForm 项目中与 EF6 code first 合作。 我使用以下方法从Db 读取实体,更新它们,然后将它们保存回Db

  1. 使用Linq to entities读取实体图(在读取DbContext处置后)
  2. 向最终用户显示读取的Entity 图表。
  3. 最终用户可以将此更改应用于Entity 图表:
    • 更新根实体
    • 添加一些子实体
    • 编辑一些子实体
    • 删除一些子实体
  4. 用户调用一个方法将他的更改持久化到Db
  5. 创建一个新的DbContext 实例。
  6. Db重新加载相同的Entity的图表
  7. 使用AutoMapper将所有属性的值从用户实体映射到重新加载的实体
  8. 使用GraphDiff将6步的结果实体附加到我的DbContext
  9. 致电DbContext.SaveChanges(); 以将更改保留到Db

    var root = new MyDbcontext()
                               .Roots
                               .LoadAggregation()
                               .ToList();
                               //    LoadAggregation in this case, means following codes:   
                               //    .Include("Child1")   
                               //    .Include("Child2")
    
    root.Child1s.Remove(child11);
    root.Child1.Add(Child13); // 
    root.Child2.Add(Child22);
    using(var uow = new UnitOfWork())   
    {
        uow.Repository<Root>().Update(root);
        uow.Repository<AnotherRoot>().Update(anotherRoot); //user may want to update multiple Roots
        uow.SaveChanges();   <---- at this point Child13.Id and  Child22.Id generated by Db
    }
    

    public void Update(Root entity) //Update method in my Repository class
    { 
       var context = new MyDbcontext();
       var savedEntity = context.Roots //reload entity graph from db
                                .LoadAggregation()
                                .ToList();
       Mapper.Map(entity,savedEntity); // map user changes to original graph
       context.UpdateGraph(savedEntity, savedEntity.MappingConfiguration); // attach updated entity to dbcontext using graphdiff
    } 

    public void SaveChanges() // SaveChanges() in UnitofWork class
    {  
      context.SaveChanges();
    }

效果很好,

在第二张图中,用户添加了 Child13 和 Child22,当我调用 uow.SaveChanges() 时,他们将保存到 Db,并且他们的 Ids 将被分配。但是entity 中的Child13.IdChild22.Id 对象是0,但我可以手动更新Ids 但我正在寻找通用 方法来更新这些Id 值与Db 生成Ids。

【问题讨论】:

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


    【解决方案1】:

    就我个人而言,我会采取稍微不同的方法。

    这样的事务不应该跨越多个上下文。即使您可以手动更新这些 ID,您也需要某种替代方法来识别这些子对象,以确保您将对象实例与数据库分配的 ID 正确同步。或者这些对象的其他方面可能在瞬态时被更新,并且也需要应用于数据库。一般来说,从不同 DBContext 加载的对象是不同的对象,即使它们具有相同的数据库标识,除非您在 EF 之上实现了某种缓存。

    我会做这样的事情。使用一个上下文,并让 EF 为您管理所有标识,方法是在您需要执行持久性操作的地方注入上下文:

    var context = new MyDbcontext();
    var root = context
                               .Roots
                               .LoadAggregation()
                               .ToList();
                               //    LoadAggregation in this case, means following codes:   
                               //    .Include("Child1")   
                               //    .Include("Child2")
    
    root.Child1.Remove(child11);
    root.Child1.Add(Child13); // 
    root.Child2.Add(Child22);
    using(var uow = new UnitOfWork())   
    {
        uow.Repository<Root>().Update(root, context);
        uow.Repository<AnotherRoot>().Update(anotherRoot, context); //user may want to update multiple Roots
        uow.SaveChanges(context);   <---- at this point Child13.Id and  Child22.Id generated by Db
    }
    
    public void Update(Root entity, MyDbcontext context) //Update method in my Repository class
    { 
       var savedEntity = context.Roots //reload entity graph from db
                                .LoadAggregation()
                                .ToList();
       Mapper.Map(entity,savedEntity); // map user changes to original graph
       context.UpdateGraph(savedEntity, savedEntity.MappingConfiguration); // attach updated entity to dbcontext using graphdiff
    } 
    
    public void SaveChanges(context) // SaveChanges() in UnitofWork class
    {  
      context.SaveChanges();
    }
    

    如果由于某种原因您无法使用相同的上下文,您可能需要考虑向子对象添加某种额外的标识字段,例如 Guid,并在持久化之前将其写入子对象通过 SaveChanges 到数据库。至少您有一种相当一致的方法来识别要同步的对象。

    【讨论】:

    • 在我的Update 方法中,我将entity 对象映射到savedEntitysavedEntity 将被更新,所以我认为您的解决方案不起作用。
    • 我认为你的第二种方式很有用,所以你能用代码解释更多吗(用于同步 Ids 的根图遍历)?
    • 我想弄清楚你在用映射器做什么。据我了解,数据映射器用于遵循上下文之外对象的 DTO 模式。但似乎您的第一个示例和我的重构正在从上下文中提取所有数据。因此,如果您要从同一个上下文中获取所有数据,则所有身份都应该是同步的,因为所有对象都位于同一个上下文池中。
    • 否则,为了解决您的第二个问题,我不确定如何在与您在此处遵循的任何模式相匹配的代码中进行说明。但核心问题是确定瞬态 ORM 对象在没有数据库身份时如何相等。我建议使用 Guid,因为它可以在提交到数据库之前生成,并且您也可以在映射到 DTO 时携带它。此时,只需查看您的持久对象(将具有 guid 和 db 生成的 ID),然后将 db ID 写入具有匹配 guid 而没有它们的对象。
    猜你喜欢
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-05
    相关资源
    最近更新 更多