Entity Framework insert object with multiple navigation properties of same type and id

c# entity-framework entity-framework-6

Question

I have read several posts about this (e.g. Why can't EF handle two properties with same foreign key, but separate references/instances?) issue but none provided a satisfying solution.

Using EF6, inserting an object with two navigation properties fails, when the properties have the same type, represent the same entry (i.e. have the same primary key value) but are two different object instances.

Setup:

public abstract class EntityBase
{
    public int Id { get; set; }
    public bool IsTransient => Id <= 0;
}

public class Person : EntityBase
{
    public string Name { get; set; }
}

public class Task : EntityBase
{
    public int CreatorId { get; set; }
    public int ReceiverId { get; set; }
    public string Text { get; set; }

    public Person Creator { get; set; }
    public Person Receiver { get; set; }
}

When I try to insert a new Task like the following, an Exception occurs when creator and receiver are the same Person (i.e. have the same Id value). Exception is: "... because more than one entity of type 'Person' have the same primary key value.".

public void AddTask(string text, Person creator, Person receiver)
{
    using (var context = new MyEntities())
    {
        Task task = new Task()
        {
            Text = text,
            Creator = creator,
            Receiver = receiver
        };
        context.Task.Add(task);
        context.Entry(creator).State = EntityState.Unchanged;
        context.Entry(receiver).State = EntityState.Unchanged; // Exception is raised here

        context.SaveChanges();
    }
}

The exception can be avoided using the following code:

public void AddTask(string text, Person creator, Person receiver)
{
    using (var context = new MyEntities())
    {
        Task task = new Task()
        {
            Text = text,
            Creator = creator,
            Receiver = receiver
        };
        context.Task.Add(task);
        context.Entry(creator).State = EntityState.Unchanged;
        if (creator.Id == receiver.Id)
            task.Receiver = creator;
        else
            context.Entry(receiver).State = EntityState.Unchanged;

        context.SaveChanges();
    }
}

Question: Is there any convenient way two avoid the exception except from checking the Id values of the navigation properties before insert?

This is a simple example given here. There are much more complex object graphs to be inserted where a manual check like shown here is quite cumbersome.

EDIT: The exception message is (german):

System.InvalidOperationException: Fehler beim Speichern oder Übernehmen der Änderungen, weil mehrere Entitäten des Typs 'Person' den gleichen Primärschlüsselwert aufweisen. Stellen Sie sicher, dass explizit festgelegte Primärschlüsselwerte eindeutig sind. Die von der Datenbank generierten Primärschlüssel müssen in der Datenbank und im Entity Framework-Modell ordnungsgemäß konfiguriert sein. Verwenden Sie den Entity Designer für die Database First-/Model First-Konfiguration. Verwenden Sie die Fluent-API 'HasDatabaseGeneratedOption' oder 'DatabaseGeneratedAttribute' für die Code First-Konfiguration.

The reason why I explicitly set the EntityState is because I'm working with detached objects. If I wouldn't set the state of the Person object it would be inserted as a new entry. To avoid this I mark it as Unchanged.

1
1
10/6/2018 12:34:56 PM

Accepted Answer

You could just set the Id's:

using (var context = new MyEntities())
{
    Task task = new Task()
    {
        Text = text,
        CreatorId = creator.Id,
        ReceiverId = receiver.Id
    };
    context.Task.Add(task);
    context.SaveChanges();
}
0
10/4/2018 2:27:44 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow