Entity Framework - The Right Way To Replace A Collection From One To Many

Suppose a customer has many phone numbers and a phone number has only one customer.

public class PhoneNumber : IValueObject { public string Number {get; set;} public string Type {get; set;} } public class Customer : IEntity { public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support public void SetPhones(params PhoneNumber[] phones) { this.phones.Clear(); this.phones.AddRange(phones); } } 

If I create an EF mapping and run it, every time I set the phone numbers, it will create new PhoneNumbers, but not delete the old ones. There are no other objects referring to phone numbers, I donโ€™t even disclose them in my dbcontext, is there any way to tell EF that Customer owns PhoneNumbers completely, and therefore, if phone numbers have been removed from the collection, they should be deleted

proof

I understand that there are a dozen ways to crack this problem, but this is not a weird edge case, like the โ€œrightโ€ way to handle it.

+5
source share
2 answers

I had the same question :)

This response to identifying relationships solved my problem.

Note. You need to load the collection (preferably, explicitly or lazily) so that it can be tracked before setting new values โ€‹โ€‹and calling save. Otherwise, you will not replace the collection, but just add it.

For instance:

 var entity = unitOfWork.EntityRepository.GetById(model.Id); // I have something like this to load collection because // I don't have the collection entities exposed to the context unitOfWork.EntityRepository.LoadCollection(entity, e => e.CollectionProperty); entity.CollectionProperty = newCollectionValuesList; unitOfWork.Save(); 

This will remove the previous collection values โ€‹โ€‹from the "collection table" and add only new values.

Hope this helps.

+6
source

First (optional):

I recommend you do

public ICollection<PhoneNumber> phones {get; private set;}

a virtual to let Entity Framework know that it should be lazy loaded (even if you don't have Lazy Load enabled, this is good practice).

public virtual ICollection<PhoneNumber> phones {get; private set;}

Second

Add the Reverse Navigation Property to your PhoneNumber class (it will be required to get the solution that I give below):

 public class PhoneNumber : IValueObject { public string Number {get; set;} public string Type {get; set;} public virtual Customer {get; set;} } public class Customer : IEntity { public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support public void SetPhones(params PhoneNumber[] phones) { this.phones.Clear(); this.phones.AddRange(phones); } } 

Third (possible solution for your problem):

Remove PhoneNumber objects from context instead from Customer :

 public ICollection<PhoneNumber> phones {get; private set;} //ew at no encapsulated collection support public void SetPhones(params PhoneNumber[] phones) { Context.PhoneNumbers.RemoveRange(this.phones); this.phones.AddRange(phones); } } 
+1
source

All Articles