Asp.net-mvc – Updating Child Entities in Entity Framework MVC

asp.net-mvcentity-framework

I am displaying in my view the Parent entity and it's children and giving the user the ability to edit both parent and child entity.

When the user clicks on save. The parent entity only gets modified while the children entities get ignored. The work around that I have is this.

var addressRepo=_dataRepositoryFactory.GetDataRepository<IPatientAddressRepository>();
foreach (var address in entity.Addresses)
{
    addressRepo.Update(address);
}

_dataRepositoryFactory.GetDataRepository<IPatientContactRepository>().Update(entity.Contact);


var guardianRepo = _dataRepositoryFactory.GetDataRepository<IPatientGuardianRepository>();
foreach (var guardian in entity.Guardians)
{
    guardianRepo.Update(guardian);
}

_dataRepositoryFactory.GetDataRepository<IPatientDemographicRepository>().Update(entity.Demographic);

return _patientRepository.Update(entity);

Is there a better method to updating all the child entities?

Best Answer

A standard pattern when applying updates to disconnected entities is as follows:

  1. Attach the root entity to the context to enable change tracking across the graph
  2. This marks the entire object graph as EntityState.Unchanged, hence you need to walk the graph and set the state accordinly
  3. Mark the parent entity as EntityState.Modified so that its changes are persisted
  4. For each child entity, work out the nature of the changes (inserts, deletions or updates) and mark their state accordingly
  5. When the context is saved, the changes across the graph will be persisted.

Adopting this approach means you can reduce your dependency requirements down to a single repository for the root entity.

For example, and assuming you are only dealing with updates:

using (var context = new MyContext())
{
    context.attach(parentEntity);
    context.Entry(parentEntity).State = EntityState.Modified;

    context.Entity(parentEntity.ChildEntity1).State = EntityState.Modified;
    context.Entity(parentEntity.ChildEntity2).State = EntityState.Modidied;

    context.SaveChanges();
}

This is often encapulated in an AttachAsModified method on your repositories, which knows how to "paint the state" of an object graph based on the root entity of the graph.

E.g.

public class MyRepository<TEntity>
{
    public void AttachAsModified(TEntity entity)
    {
        _context.attach(entity);
        _context.Entry(entity).State = EntityState.Modifed;
        _context.Entity(entity.ChildEntity1).State = EntityState.Modified;
        // etc
        _context.SaveChanges();
    }   
}

There is additional complexity if you need to consider inserts or deletes of child entities. These boil down to loading the current state of the root entity and its children and then comparing the child sets to the sets on the updated root entity. The state is then set to EntityState.Deleted or EntityState.Added depending on the overlap of the sets.

NB code typed straight into the browser so there may/will be some typos.