High Plains Grifter
High Plains Grifter

Reputation: 1571

EF6 Primary Key Violation When adding Complex Item

I have an Entity set of classes where there is a recipe; that recipe can have multiple Ingredients (handled with a foreign key), each ingredient can have a component. There are several sets of tables that follow a pattern similar to this, which several layers

When I add a single recipe, which contains some ingredients that ultimately lead to the same component, I get an error message saying that adding the recipe to the database causes a primary key violation on the components table. Is there a way to tell Entity that if a component has the same primary key value, it should be updated not inserted? I get that you can do this at the top level using context.Recipes.AddOrUpdate(recipe), but how could I achieve it for each of the sub levels down into the tree structure?

here are some summarised examples of the entity classes that I have in place:

public partial class Recipe
{
    public int Id { get; set; }
    public virtual ICollection<Ingredient> Ingredients { get; set; }
}

public partial class Ingredient
{
    public int Id {get;set;}
    public int ComponentId { get; set; }
    public virtual Component Component { get; set; }
    public virtual ICollection<Recipe> Recipes { get; set; }
}

public partial class Component
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public string Description { get; set; }
    public virtual ICollection<Ingredient> Ingredients { get; set; }
}

//in the : DbContext class, there is this:

    modelBuilder.Entity<Ingredient>()
        .HasMany(e => e.Recipes)
        .WithMany(e => e.Ingredients)
        .Map(m => m.ToTable("Recipe_Ingredient_Link").MapLeftKey("IngredientId").MapRightKey("RecipeId"));

Am I receiving this error message because the two components with the same Id have got different Ingredients ICollections associated with them, causing Entity to treat them as different objects? If so, how can I get round this problem? If not, what is the problem?

I generated the classes from an existing database and haven't changed the code it made for me, other than to turn off lazy loading.

Upvotes: 1

Views: 48

Answers (1)

Xerillio
Xerillio

Reputation: 5261

I think your problem here is that in your conversion from JSON to C# objects you get a separate object for each component in the JSON. This means even if two components have the same ID it's two different objects like you have also described. This is as illustrated here:

Recipe #1
|
|-- Ingr. #1
|   |-- Comp. #1
|
|-- Ingr. #2
|   |-- Comp. #2
|
|-- Ingr. #3
    |-- Comp. #1  // Same ID as the one for Ingr. 1, but it's a separate C# object

When you try to add a hierarchy of objects (by just adding the top-level Recipe) Entity Framework will see each C# object as a separate entity. So even if two objects share the same ID, there's no logic telling EF how to handle that. I.e. how should it behave if the other properties (apart from the ID) are different?

So what I believe you would need to do instead is to ensure that no two objects have conflicting IDs. You have to iterate through your objects and make sure that if two components have the same ID, you need to remove one of them and have the Ingredients link to the same Component object, as illustrated here:

Recipe #1
|
|-- Ingr. #1
|   |----------------|
|                    |
|-- Ingr. #2         |
|   |-- Comp. #2     |-- Comp. #1 // Now they point to the same C# object
|                    |
|-- Ingr. #3         |
    |----------------|

I'll let you think about how to code that.

Upvotes: 1

Related Questions