Entity 4.1 Updating an existing parent entity with new child Entities
Solution 1:
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();
}
Solution 2:
I found this recent article on the GraphDiff extension for DbContext.
Apparently it is a generic, reusable variant of Slauma's solution.
Example code:
using (var context = new TestDbContext())
{
// Update DBcompany and the collection the company and state that the company 'owns' the collection Contacts.
context.UpdateGraph(company, map => map.OwnedCollection(p => p.Contacts));
context.SaveChanges();
}
On a side note; I see the author has proposed to the EF team to use his code in issue #864 Provide better support for working with disconnected entities.