简体   繁体   中英

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

I have read several posts about this (eg 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 (ie 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 (ie 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 .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM