I first clear the existing ingredients collection in the product
entity and than add the updated list of ingredients again.
Well, this is kind of brute-force-attack to update the child collection. EF doesn’t have any magic to update the children – which means: adding new children, deleting removed children, updating existing children – by only setting the state of the parent to Modified
. Basically this procedure forces you to delete the old children also from the database and insert the new one, like so:
// product is the detached product with the detached new children collection
using (var context = new MyContext())
{
var productInDb = context.Products.Include(p => p.Ingredients)
.Single(p => p.Id == product.Id);
// Update scalar/complex properties of parent
context.Entry(productInDb).CurrentValues.SetValues(product);
foreach (var ingredient in productInDb.Ingredients.ToList())
context.Ingredients.Remove(ingredient);
productInDb.Ingredients.Clear(); // not necessary probably
foreach (var ingredient in product.Ingredients)
productInDb.Ingredients.Add(ingredient);
context.SaveChanges();
}
The better procedure is to update the children collection in memory without deleting all children in the database:
// product is the detached product with the detached new children collection
using (var context = new MyContext())
{
var productInDb = context.Products.Include(p => p.Ingredients)
.Single(p => p.Id == product.Id);
// Update scalar/complex properties of parent
context.Entry(productInDb).CurrentValues.SetValues(product);
var ingredientsInDb = productInDb.Ingredients.ToList();
foreach (var ingredientInDb in ingredientsInDb)
{
// Is the ingredient still there?
var ingredient = product.Ingredients
.SingleOrDefault(i => i.Id == ingredientInDb.Id);
if (ingredient != null)
// Yes: Update scalar/complex properties of child
context.Entry(ingredientInDb).CurrentValues.SetValues(ingredient);
else
// No: Delete it
context.Ingredients.Remove(ingredientInDb);
}
foreach (var ingredient in product.Ingredients)
{
// Is the child NOT in DB?
if (!ingredientsInDb.Any(i => i.Id == ingredient.Id))
// Yes: Add it as a new child
productInDb.Ingredients.Add(ingredient);
}
context.SaveChanges();
}