В приведенном ниже примере кода я получаю следующее исключение при выполнении db.Entry(a).Collection(x => x.S).IsModified = true
:
System.InvalidOperationException: «Экземпляр типа сущности« B »не может быть отслежен, поскольку другой экземпляр со значением ключа« {Id: 0} »уже отслеживается. При подключении существующих объектов убедитесь, что подключен только один экземпляр объекта с данным значением ключа.
Почему он не добавляет, а не прикрепляет экземпляры B?
Странно, что документация IsModified
не указывает InvalidOperationException
как возможное исключение. Неверная документация или ошибка?
Я знаю, что этот код странный, но я написал его только для того, чтобы понять, как работает ядро в некоторых странных случаях egde. То, что я хочу, это объяснение, а не обходной путь.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public class A
{
public int Id { get; set; }
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
}
public class B
{
public int Id { get; set; }
}
public class Db : DbContext {
private const string connectionString = @"Server=(localdb)\mssqllocaldb;Database=Apa;Trusted_Connection=True";
protected override void OnConfiguring(DbContextOptionsBuilder o)
{
o.UseSqlServer(connectionString);
o.EnableSensitiveDataLogging();
}
protected override void OnModelCreating(ModelBuilder m)
{
m.Entity<A>();
m.Entity<B>();
}
}
static void Main(string[] args)
{
using (var db = new Db()) {
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Add(new A { });
db.SaveChanges();
}
using (var db = new Db()) {
var a = db.Set<A>().Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
db.SaveChanges();
}
}
}
Ответы:
Причина ошибки в приведенном коде заключается в следующем.
Когда вы получаете созданную сущность
A
из базы данных, ее свойствоS
инициализируется коллекцией, которая содержит две новые записиB
.Id
каждого из этих новыхB
объектов равен0
.После выполнения строки кода
var a = db.Set<A>().Single()
коллекцияS
сущностейA
не содержитB
сущностей из базы данных, посколькуDbContext Db
не использует отложенную загрузку и нет явной загрузки коллекцииS
. СущностьA
содержит только новыеB
сущности, которые были созданы во время инициализации коллекцииS
.Когда вы вызываете
IsModifed = true
коллекциюS
сущностей, структура пытается добавить эти два новых объектаB
в отслеживание изменений. Но это терпит неудачу, потому что оба новыхB
объекта имеют то же самоеId = 0
:Из трассировки стека видно, что структура сущностей пытается добавить
B
сущности вIdentityMap
:И сообщение об ошибке также говорит, что он не может отслеживать
B
сущность,Id = 0
потому что другаяB
сущность с таким жеId
уже отслежена.Как решить эту проблему.
Чтобы решить эту проблему, вы должны удалить код, который создает
B
сущности при инициализацииS
коллекции:Вместо этого вы должны заполнить
S
коллекцию на месте, гдеA
создается. Например:Если вы не используете отложенную загрузку, вам следует явно загрузить
S
коллекцию, чтобы добавить ее элементы в отслеживание изменений:Короче говоря , они прикреплены вместо добавления, потому что они имеют
Detached
состояние.После выполнения строки кода
созданные экземпляры объекта
B
имеют состояниеDetached
. Это можно проверить с помощью следующего кода:Затем, когда вы установите
EF пытается добавить
B
объекты для отслеживания изменений. Из исходного кода EFCore вы можете видеть, что это приводит нас к методу InternalEntityEntry.SetPropertyModified со следующими значениями аргумента:property
- одна из нашихB
организаций,changeState = true
,isModified = true
,isConceptualNull = false
,acceptChanges = true
,Этот метод с такими аргументами изменяет состояние
Detached
B
объектовModified
, а затем пытается начать их отслеживание (см. Строки 490 - 506). ПосколькуB
сущности теперь имеют состояние,Modified
это приводит к их присоединению (не добавлению).источник
S
должна быть загружена явно, поскольку предоставленный код не использует отложенную загрузку. Конечно, EF сохранил ранее созданныеB
объекты в базе данных. Но строка кодаA a = db.Set<A>().Single()
загружает только сущностьA
без сущностей в коллекцииS
. Для загрузки коллекцииS
нужно использовать готовую загрузку. Я изменю свой ответ так, чтобы он явно включал ответ на вопрос «Почему он не добавляет вместо того, чтобы прикреплять экземпляры B?».