EF Core пытается обновить несуществующую запись - PullRequest
1 голос
/ 06 августа 2020

ОБНОВЛЕНИЕ: был код, который я не видел до тех пор, пока не создал этот пост, из-за которого моя вставка не удалась. Теперь все работает как положено. Приносим извинения и благодарим вас за время, потраченное на помощь.

У меня проблема, когда EF пытается обновить несуществующую запись. Мне нужно вставить запись.

Пример кода ниже:

DoStuff(List<ParentObj> listParent, List<OtherParent> listOtherParent)
{
    foreach(var op in otherParent)
    {
        var updateThisParentRecord = listParent.FirstOrDefault(x => x.Id == op.Id);
        if(updateThisParentRecord != null)
        {
            updateThisParentRecord.ChildRecordList.Add(new ChildRecord
            {
                //set relevant props not PK as it is an identity column
                OtherChildObject = new OtherChildObject 
                {
                    //set relevant props not PK as it is an identity column
                }
            });
        }
    }
    await _parentObjectContext.SaveChangesAsync();
}

Код модели:

public partial class ParentObj
{
    public ParentObj()
    {
        ChiledRecordList = new HashSet<ChildRecord>();
    }

    public int Id {get;set;}
    public virtual ICollection<ChildRecord> ChildRecordList {get;set;}
}

public partial class ChildRecord
{
    public int Id {get;set;}
    public int ParentId {get;set;}
    public int OtherChildObject {get;set;}

    public virtual OtherChildObject OtherChildObject {get;set;}
    public virtual ParentObj {get;set;}
}

public partial class OtherChildObject
{
    public OtherChildObj()
    {
        ParentObj = new HashSet<ParentObj>();
        ChildRecord = new Hashset<ChildRecord>();
    }

    public long Id {get;set;}
    //now that I have written this out, the below line seems strange and may 
    //be keyed wrong?
    public virtual ICollection<ParentObj> ParentObj {get;set;}
    public virtual ICollection<ChildRecord> ChildRecord {get;set;}
}

При сохранении возникает исключение ниже:

"Ожидается, что операция с базой данных повлияет на 1 строку (строки), но на самом деле повлияла на 0 строк. Данные могли быть изменены или удалены после загрузки объектов. См. http://go.microsoft.com/fwlink/?LinkId=527962 для получения информации о понимании и обработка исключений optimisti c concurrency. "

Это то, что находится в списке записей исключения: {{Id: 1000} Modified EntityType: ChildRecord}

Сгенерированный SQL из EF по праву создает OtherChildObject, но пытается обновить несуществующий ChildRecord. Кто-нибудь знает что происходит? Заранее спасибо

1 Ответ

2 голосов
/ 06 августа 2020

Критическое изменение с DetectChanges учитывает значения ключей, сгенерированные хранилищем :

Старое поведение

До EF Core 3.0 неотслеживаемая сущность, обнаруженная с помощью DetectChanges, будет отслеживается в состоянии добавлено и вставляется как новая строка при вызове SaveChanges.

Новое поведение

Начиная с EF Core 3.0, если сущность использует сгенерированные значения ключей и задано некоторое значение ключа , то объект будет отслеживаться в состоянии Modified. Это означает, что строка для объекта предполагается существующей и будет обновлена ​​при вызове SaveChanges. Если значение ключа не установлено или если тип объекта не использует сгенерированные ключи, то новый объект будет по-прежнему отслеживаться как добавленный, как и в предыдущих версиях.

Почему

Это изменение было сделано для упрощения и большей согласованности работы с отключенными графами сущностей при использовании ключей, сгенерированных хранилищем. сгенерированные ключи, но значения ключей явно установлены для новых экземпляров. Исправление состоит в том, чтобы явно настроить ключевые свойства, чтобы не использовать сгенерированные значения. Например, с текучим API:

modelBuilder
    .Entity<Blog>()
    .Property(e => e.Id)
    .ValueGeneratedNever();

Или с аннотациями данных:

[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }

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

...