EF5 code first with ASP.NET Web API: update an object with many many-to-many relationships

I am trying to update Customer in my database using ASP.NET Web API and Entity Framework 5 with code, but it does not work. My objects look like this:

 public class CustomerModel { public int Id { get; set; } public string Name { get; set; } // More fields public ICollection<CustomerTypeModel> CustomerTypes { get; set; } } public class CustomerTypeModel { public int Id { get; set; } public string Type { get; set; } [JsonIgnore] public ICollection<CustomerModel> Customers { get; set; } } 

Nothing special. I created a web interface where users can add a client by specifying a name and checking one or more types of clients. When I click the Submit button, the data is sent to my Web API method:

 public void Put([FromBody]CustomerModel customer) { using (var context = new MyContext()) { context.Customers.Attach(customer); context.Entry(customer).State = EntityState.Modified; context.SaveChanges(); } } 

This updates the client fields, but the corresponding client types are ignored. The incoming Customer object contains a list of CustomerTypes , it must be associated with:

 [0] => { Id: 1, Type: "Finance", Customers: Null }, [1] => { Id: 2, Type: "Insurance", Customers: Null } [2] => { Id: 3, Type: "Electronics", Customers: Null } 

But instead of looking at this list and adding / removing related objects, EF just ignores it. New associations are ignored, and existing associations remain, even if they must be deleted.

I had a similar problem when inserting a client into the database, this was fixed when I changed the state of these objects to EntityState.Unchanged . Naturally, I tried to apply this most magical fix in my update script:

 public void Put([FromBody]CustomerModel customer) { using (var context = new MyContext()) { foreach (var customertype in customer.CustomerTypes) { context.Entry(customertype).State = EntityState.Unchanged; } context.Customers.Attach(customer); context.Entry(customer).State = EntityState.Modified; context.SaveChanges(); } } 

But EF continues to show the same behavior.

Any ideas on how to fix this? Or should I just do a manual cleanup in the CustomerTypes list and then manually add them?

Thanks in advance.

In JP

+6
source share
2 answers

This is not solvable, only setting the state of entities. First you need to load the client from the database, including all its current types, and then delete the types or add types to the loaded client in accordance with the updated collection of types of the sent client. Tracking changes will do the rest to remove entries from the connection table or insert new entries:

 public void Put([FromBody]CustomerModel customer) { using (var context = new MyContext()) { var customerInDb = context.Customers.Include(c => c.CustomerTypes) .Single(c => c.Id == customer.Id); // Updates the Name property context.Entry(customerInDb).CurrentValues.SetValues(customer); // Remove types foreach (var typeInDb in customerInDb.CustomerTypes.ToList()) if (!customer.CustomerTypes.Any(t => t.Id == typeInDb.Id)) customerInDb.CustomerTypes.Remove(typeInDb); // Add new types foreach (var type in customer.CustomerTypes) if (!customerInDb.CustomerTypes.Any(t => t.Id == type.Id)) { context.CustomerTypes.Attach(type); customerInDb.CustomerTypes.Add(type); } context.SaveChanges(); } } 
+10
source

A cleaner solution would be:

 public void Put([FromBody]CustomerModel customer) { using (var context = new MyContext()) { var customerInDb = context.Customers.Include(c => c.CustomerTypes) .Single(c => c.Id == customer.Id); // Updates the Name property context.Entry(customerInDb).CurrentValues.SetValues(customer); // Remove types customer.CustomerTypes.Clear(); // Add new types foreach (var type in customer.CustomerTypes) { context.CustomerTypes.Attach(type); customerInDb.CustomerTypes.Add(type); } context.SaveChanges(); } } 
0
source

Source: https://habr.com/ru/post/945144/