Masoud
Masoud

Reputation: 8181

Update entity graph Ids with Db generated Ids in Disconnected environment(EF)

I'm working with EF6 code first in a WinForm project. I used following method for reading entities from Db, updating them and then save back them to Db:

  1. Read Entity graph using Linq to entities(after reading the DbContext disposes)
  2. Show readed Entity graph to end user.
  3. End user may apply this changes to Entity graph:
    • Update root entity
    • Add some child entities
    • Edit some child entities
    • Delete some child Entities
  4. User call a method to persist his changes to Db
  5. Create a new DbContext instance.
  6. Reload same Entity's graph from Db
  7. Map the all property's value from user entity to reloaded entity using AutoMapper
  8. Attach the result entity of 6 step to my DbContext using GraphDiff
  9. Call DbContext.SaveChanges(); to persist changes to Db enter image description here

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

It works fine,

In second graph the Child13 and Child22 added by user and when I call uow.SaveChanges() they will save to Db and their Ids will be assign. but Child13.Id and Child22.Id in entity objects are 0 yet, I could manually update the Ids but I'm looking for generic way to update these Id values with Db generated Ids.

Upvotes: 3

Views: 895

Answers (1)

MutantNinjaCodeMonkey
MutantNinjaCodeMonkey

Reputation: 1249

Personally, I'd take a slightly different approach.

A transaction like this shouldn't span multiple contexts. Even if you could update those ID's manually, you'd need some sort of alternate way of identifying those child objects to ensure you synced up the object instance correctly with the ID assigned by your database. Or it's possible other aspects of those objects were updated while transient, and will need to be applied to the database as well. Generally speaking, objects loaded from different DBContexts are different objects, even if they have the same database identity unless you've implemented some sort of caching on top of EF.

I'd do something like this. Use one context, and let EF manage all of the identify for you by injecting the context where you need to do persistence operations:

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

If you're unable to use the same context for some reason, You may want to consider adding an additional identity field of some sort to the child object, such as a Guid, and writing that to the child prior to persisting back to the database via SaveChanges. At least you'd have a fairly consistent means of identifying the objects for syncing.

Upvotes: 3

Related Questions