Here’s my situation – I’ve got a DB which has some tables named recipes, ingredients and recipes_ingredients.
Recipes are composed of 1+ ingredients.
The recipes_ingredients has FKs between the recipes and ingredients table.
The classes that get generated are recipe and ingredient and recipe has a navigation property that looks like so:
public virtual ICollection<ingredients> ingredients { get; set; }
Great, I understand that I get a generated recipe class and a generated ingredient class and that the recipes_ingredients table doesn’t get a class generated since EF views this simply as a navigation property.
Now, I’ve got a function called SetIngredientsForRecipe that looks like so (minus the try-catch code for brevity’s sake:
public void SetIngredientsForRecipe(long recipeId, List<string> ingredients)
{
using (var db = new FoodEntities(ConnectionString, null, null))
{
var existing = GetCurrentIngredients(recipeId);
var toRemove = existing.Except(ingredients);
var toAdd = ingredients.Except(existing);
var recipe = db.recipes.Where(r => r.Id == recipeId).FirstOrDefault();
foreach (var name in toRemove)
{
var entry = recipe.ingredients.Where(i => i.Name == name).FirstOrDefault();
recipe.ingredients.Remove(entry);
}
foreach (var name in toAdd)
{
var entry = db.ingredients.Where(i => i.Name == name).FirstOrDefault();
recipe.ingredients.Add(entry);
}
db.SaveChanges();
}
}
The intent, as the name suggests, is to update the ingredient list for the given recipe to only whatever is in the list. I’m still getting comfortable with EF and wondering if there’s a better (more efficient?) way to accomplish what I’m trying to do.
Follow-up:
Following the suggestions by ntziolis below, I opted to use
recipe.ingredients.Clear() to clear out whatever was in the recipe/ingredient mapping and then use the mocking that was mentioned to quickly add the new ones. Something like this:
foreach (var name in ingredients)
{
// Mock an ingredient since we just need the FK that is referenced
// by the mapping table - the other properties don't matter since we're
// just doing the mapping not inserting anything
recipe.ingredients.Add(new Ingredient()
{
Name = name
});
}
and this works very nicely.
General performance guidelines are:
Containsin order to simplify and speed up your codeBased on these principles here is a optimized (not simpler though) solution to your problem:
If you want it simpler you could just clear all ingredients and re add them if necessary, EF might even be clever enough to figure out that the relations haven’t changed, not sure about it though:
UPDATE
Some coding errors have been corrected.