4
votes

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?

2
Please share your Patient class definition, and your IPatientRepository implementation.B2K

2 Answers

5
votes

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.

2
votes

I remember having an issue similar to this when updating both the parent and children in one fell swoop. I didn't really spend any time figuring out the exact cause, but I soon realised that passing massive models back with a ton of changes perhaps wasn't the best model. In one extreme case my post was so large it was exceeding the default message size.

My preferred method of updating large parent/child models like this is to pass the whole parent/ child model down to the page and then update each child separately via ajax. So the parent and child are never updated in the same action. It also makes handling errors easier, if you pass a massive model up and one child fails validation then it can sometimes be difficult to pass that error back down in collections etc in a manner thats obvious to the user. Whereas doing a single update is always easier for the user to spot / handle.

I would urge you to use View Models instead of passing your entities into your view directly, this is considered bad practice for all but the simplest views. You can slim down your models to reduce the payload to the view, you can also easily extend your view model in the future to add extra functionality without it affecting your entity at all.

Just some thoughts, hope it helps.