Entity 4.1 Updating an existing parent entity with new child Entities

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();
}

Leave a Comment