EF7 How to handle update operation for nested objects

I am trying to figure out best practices when updating an entity and all its children. For instance; I have an employer update service that updates the entity of the employer and the entity "Address" of the employer and the "Telephone" entities of each "Address". The user can add new addresses to the existing employer, or they can update the current addresses or delete them, the same applies to the phones of each address. Could you help me write the perfect code that will handle this script.

I am using EF7 rc1 and I am using Automapper to map Dto to Entity in my service.

public partial class Employer { public int EmployerId { get; set; } public int Name { get; set; } [InverseProperty("Employer")] public virtual ICollection<Address> Address { get; set; } } public partial class Address { public int AddressId { get; set; } public int Address1{ get; set; } public int City { get; set; } [ForeignKey("EmployerId")] [InverseProperty("Address")] public virtual Employer Employer { get; set; } [InverseProperty("Address")] public virtual ICollection<Phone> Phone { get; set; } } public partial class Phone { public int PhoneId { get; set; } public string Number { get; set; } [ForeignKey("AddressId")] [InverseProperty("Phone")] public virtual Address Address { get; set; } } 

My service method;

 public async Task<IServiceResult> Update(EmployerDto employer) { var employerDbEntity = await _db.Employer .Include(a=>a.Address).ThenInclude(p=>p.Phone) .SingleOrDefaultAsync (a=>a.EmployerId == employer.EmployerId); //How to handle the update operation for children? var entity = Mapper.Map<Employer>(employer); HandleChildren(employerDbEntity,entity); await _db.SaveChangesAsync(); ... ... } private void HandleChildren(Employer employerDbEntity,Employer entity) { //Delete foreach (var existing in employerDbEntity.Address.ToList()) { if (!entity.Address.Any(a => a.AddressId == existing.AddressId)) employerDbEntity.Address.Remove(existing); } //Update or Insert foreach (var address in entity.Address) { var existing = employerDbEntity.Address.SingleOrDefault(a =>a.AddressId == address.AddressId); //Insert if (existing == null) { employerDbEntity.Address.Add(address); } //Update else { Mapper.Map(address, existing); } } } 
+6
source share
1 answer

This example looks like a decent way to handle child collections. Each check must be checked manually for the actions performed. (Using generics sounds good, but always bites off in some way. Usually, performance.)

With that in mind, here are a few recommendations:

  • Move child collection processing to separate methods / services.
  • If the query is for existing entities, retrieve the entire collection in one query, repeat the results in memory.
  • Since you write asynchronous code, you can use parallel processing of child collections! To do this, each operation must create its own context. This explains why it is faster.

Here is an example of using recommendations:

 private async Task UpdateAddresses(List<Address> addressesToUpdate) { using(var context = new Context()) { var existingAddressIds = await context.Addresses .Where(a => addressesToUpdate.Contains(a.AddressId)) .ToListAsync() .ConfigureAwait(false); existingAddressIds.ForEach(a => context.Addresses.Remove(a)); await context.SaveChangesAsync().ConfigureAwait(false); } } 
0
source

All Articles