Я прочитал несколько сообщений об этом (например, Почему EF не может обрабатывать два свойства с одним и тем же внешним ключом, но с отдельными ссылками / экземплярами?), но ни один из них не дал удовлетворительного решения.

При использовании EF6 при вставке объекта с двумя свойствами навигации происходит сбой, когда свойства имеют один и тот же тип, представляют одну и ту же запись (т. Е. Имеют одинаковое значение первичного ключа), но являются двумя разными экземплярами объекта.

Настроить:

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

Когда я пытаюсь вставить новую задачу, подобную следующей, возникает исключение, когда creator и receiver совпадают Person (т. е. имеют одинаковое значение Id) . Исключение: «... потому что несколько сущностей типа« Человек »имеют одно и то же значение первичного ключа.».

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

Исключения можно избежать, используя следующий код:

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

Вопрос: Есть ли какой-либо удобный способ избежать исключения, кроме проверки значений Id свойств навигации перед вставкой?

Это простой пример, приведенный здесь. Есть гораздо более сложные графы объектов, которые нужно вставить, где ручная проверка, как показано здесь, довольно громоздко.

РЕДАКТИРОВАТЬ: Сообщение об исключении (на немецком языке):

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 exploisit 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 для первой конфигурации базы данных / модели. Verwenden Sie die Fluent-API HasDatabaseGeneratedOption или DatabaseGeneratedAttribute для первой конфигурации кода.

Причина, по которой я явно устанавливаю EntityState, заключается в том, что я работаю с отсоединенными объектами. Если бы я не установил состояние объекта Person, он был бы вставлен как новая запись. Чтобы этого не произошло, я помечу его как Unchanged.

1
Abid 4 Окт 2018 в 16:35

1 ответ

Лучший ответ

Вы можете просто установить идентификатор:

using (var context = new MyEntities())
{
    Task task = new Task()
    {
        Text = text,
        CreatorId = creator.Id,
        ReceiverId = receiver.Id
    };
    context.Task.Add(task);
    context.SaveChanges();
}
0
Stefan 4 Окт 2018 в 14:27