Причина помилки у наданому коді наступна.
Коли ви створюєте об'єкт A
із бази даних, його властивість S
ініціалізується колекцією, що містить дві нові записи B
. Id
кожного з цих нових B
утворень дорівнює 0
.
// This line of code reads entity from the database
// and creates new instance of object A from it.
var a = db.Set<A>().Single();
// When new entity A is created its field S initialized
// by a collection that contains two new instances of entity B.
// Property Id of each of these two B entities is equal to 0.
public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B {} };
Після виконання рядка var a = db.Set<A>().Single()
збору коду S
сутність A
не містить B
об'єктів із бази даних, тому DbContext Db
що не використовує ледачу завантаження і немає явного завантаження колекції S
. Суб'єкт A
містить лише нові B
об'єкти, які були створені під час ініціалізації колекції S
.
Коли ви звертаєтесь IsModifed = true
до структури сукупності колекції, він S
намагається додати ці два нові елементи B
у відстеження змін. Але це не вдається, тому що обидва нові B
суб'єкти мають однакове Id = 0
:
// This line tries to add to change tracking two new B entities with the same Id = 0.
// As a result it fails.
db.Entry(a).Collection(x => x.S).IsModified = true;
З трасування стека видно, що структура сутності намагається додати B
об'єкти до IdentityMap
:
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified, Boolean isConceptualNull, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(InternalEntityEntry internalEntityEntry, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.SetFkPropertiesModified(Object relatedEntity, Boolean modified)
at Microsoft.EntityFrameworkCore.ChangeTracking.NavigationEntry.set_IsModified(Boolean value)
І повідомлення про помилку також говорить про те, що воно не може відстежувати B
сутність, Id = 0
оскільки інша B
сутність з такою ж Id
уже відслідковується.
Як вирішити цю проблему.
Щоб вирішити цю проблему, слід видалити код, який створює B
сутності при ініціалізації S
колекції:
public ICollection<B> S { get; set; } = new List<B>();
Замість цього слід заповнити S
колекцію у тому місці, де A
створено. Наприклад:
db.Add(new A {S = {new B(), new B()}});
Якщо ви не використовуєте ледаче завантаження, вам слід явно завантажити S
колекцію, щоб додати її елементи до відстеження змін:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Чому він не додає замість приєднання екземплярів B?
Коротше кажучи , вони додаються, щоб їх додавали, оскільки вони мають Detached
державну.
Після виконання рядка коду
var a = db.Set<A>().Single();
створені екземпляри суб'єкта B
мають стан Detached
. Це можна підтвердити за допомогою наступного коду:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Потім, коли ви встановите
db.Entry(a).Collection(x => x.S).IsModified = true;
EF намагається додати B
об'єкти для зміни відстеження. З вихідного коду EFCore видно, що це призводить нас до методу InternalEntityEntry.SetPropertyModified із наступними значеннями аргументу:
property
- одна з наших B
організацій,
changeState = true
,
isModified = true
,
isConceptualNull = false
,
acceptChanges = true
.
Цей метод з такими аргументами змінює стан Detached
B
entites до Modified
, а потім намагається почати їх відстеження (див. Рядки 490 - 506). Оскільки B
суб'єкти мають стан, Modified
це призводить до того, що вони додаються (не додаються).