Entity Framework - Add or Update Parent Entity with Children This code saves an DomainEntityRecord (parent) and DomainEntityDetailRecords (child), and it works. It feels odd that I get the record from the DB, then remove the entities that don't exist in the domain model version, then when I save, I save (AddOrUpdate) the domain entities, and not the EF version. Maybe this is the right way?

public void Handle(SaveDomainEntity message)
{
  var domainEntityRecord = mapper.Map&lt;DomainEntityRecord&gt;(message);

  var existingDomainEntity = dbContext.Set&lt;DomainEntityRecord&gt;()
    .Where(x =&gt; x.Id == domainEntityRecord.Id)
    .Include(x =&gt; x.DomainEntityDetailRecords)
    .SingleOrDefault();

  if (existingDomainEntity != null)
  {
    // Delete detail records that no longer exist.
    foreach (var existingDetail in existingDomainEntity.DomainEntityDetailRecords.ToList())
    {
      if (domainEntityRecord.DomainEntityDetailRecords.All(
        x =&gt; x.DetailId != existingDetail.DetailId))
      {
        dbContext.Set&lt;DomainEntityDetailRecord&gt;().Remove(existingDetail);
      }
    }
  }

  dbContext.Set&lt;DomainEntityRecord&gt;().AddOrUpdate(domainEntityRecord);

  domainEntityRecord.DomainEntityDetailRecords.ForEach(
    record =&gt; dbContext.Set&lt;DomainEntityDetailRecord&gt;().AddOrUpdate(record));

  dbContext.SaveChanges();
}
The line ...


 dbContext.Set&lt;DomainEntityRecord&gt;().AddOrUpdate(domainEntityRecord);



... will always try to find an existing record in the database in order to determine whether domainEntityRecord should be marked as Added or as Modified.

But you already fetch the existing record by the statement:


var existingDomainEntity = dbContext.Set&lt;DomainEntityRecord&gt;()
   .Where(x =&gt; x.DomainEntityId == domainEntityRecord.DomainEntityId)
   .Include(x =&gt; x.DomainEntityDetailRecords)
   .SingleOrDefault();



So there is a redundant database roundtrip in your code. After this statement var existingDomainEntity = ... you know everything that AddOrUpdate is going to find out again. So you may as well do it yourself: if the record exist: modify it and its details, if it doesn't: add it. To modify the existing records, use CurrentValues.SetValues:

  var domainEntityRecord = mapper.Map&lt;DomainEntityRecord&gt;(message);

  var existingDomainEntity = dbContext.Set&lt;DomainEntityRecord&gt;()
    .Where(x =&gt; x.DomainEntityId == domainEntityRecord.DomainEntityId)
    .Include(x =&gt; x.DomainEntityDetailRecords)
    .SingleOrDefault();

  if (existingDomainEntity != null)
  {
    // Delete detail records that no longer exist.
    foreach (var existingDetail in existingDomainEntity.DomainEntityDetailRecords.ToList())
    {
      if (domainEntityRecord.DomainEntityDetailRecords.All(
        x =&gt; x.DomainEntityDetailId != existingDetail.DomainEntityDetailId))
      {
        dbContext.Set&lt;DomainEntityDetailRecord&gt;().Remove(existingDetail);
      }
    }
    // Copy current (incoming) values to db entry:
    dbContext.Entry(existingDomainEntity).CurrentValues.SetValues(domainEntityRecord);
    var detailPairs = from curr in domainEntityRecord.DomainEntityDetailRecords
                      join db in existingDomainEntity.DomainEntityDetailRecords 
                        on curr.DomainEntityDetailId equals db.DomainEntityDetailId into grp
                      from db in grp.DefaultIfEmpty()
                      select new { curr, db };
    foreach(var pair in detailPairs)
    {
      if (pair.db != null)
        dbContext.Entry(pair.db).CurrentValues.SetValues(pair.curr);
      else
        dbContext.Set&lt;DomainEntityDetailRecord&gt;().Add(pair.curr);
    }
  }
  else
  {
    dbContext.Set&lt;DomainEntityRecord&gt;().Add(domainEntityRecord);
    // This also adds its DomainEntityDetailRecords
  }

  dbContext.SaveChanges();


As you see, for the details I use a GroupJoin (join - into which serves as an outer join) to determine the existing and the new details. The existing ones are modified, the new ones are added.