Przyczyna błędu w podanym kodzie jest następująca.
Po utworzeniu encji A
z bazy danych jej właściwość S
jest inicjowana kolekcją zawierającą dwa nowe rekordy B
. Id
każdego z tych nowych B
bytów jest równy 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 {} };
Po wykonaniu wiersza kodu var a = db.Set<A>().Single()
zbiór S
encji A
nie zawiera B
encji z bazy danych, ponieważ DbContext Db
nie używa leniwego ładowania i nie ma jawnego ładowania kolekcji S
. Jednostka A
zawiera tylko nowe B
jednostki, które zostały utworzone podczas inicjowania kolekcji S
.
Podczas wywoływania środowiska encji IsModifed = true
kolekcji S
próbuje dodać te dwa nowe elementy B
do śledzenia zmian. Ale zawodzi, ponieważ oba nowe B
podmioty mają to samo 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;
Ze śladu stosu widać, że struktura encji próbuje dodać B
encje do 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)
Komunikat o błędzie mówi również, że nie może śledzić B
encji, Id = 0
ponieważ inna B
encja z tym samym Id
jest już śledzona.
Jak rozwiązać ten problem.
Aby rozwiązać ten problem, należy usunąć kod, który tworzy B
jednostki podczas inicjowania S
kolekcji:
public ICollection<B> S { get; set; } = new List<B>();
Zamiast tego należy wypełnić S
kolekcję w miejscu, w którym A
został utworzony. Na przykład:
db.Add(new A {S = {new B(), new B()}});
Jeśli nie używasz leniwego ładowania, należy jawnie załadować S
kolekcję, aby dodać jej elementy do śledzenia zmian:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Dlaczego nie dodaje zamiast instancji B?
Krótko mówiąc , są dołączeni natychmiast po dodaniu, ponieważ mają Detached
stan.
Po wykonaniu wiersza kodu
var a = db.Set<A>().Single();
utworzone wystąpienia encji B
mają stan Detached
. Można to zweryfikować za pomocą następnego kodu:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Potem, kiedy ustawisz
db.Entry(a).Collection(x => x.S).IsModified = true;
EF próbuje dodać B
podmioty, aby zmienić śledzenie. Z kodu źródłowego EFCore widać, że prowadzi nas to do metody InternalEntityEntry.SetPropertyModified z następnymi wartościami argumentów:
property
- jeden z naszych B
podmiotów,
changeState = true
,
isModified = true
,
isConceptualNull = false
,
acceptChanges = true
.
Ta metoda z takimi argumentami zmienia stan Detached
B
entites Modified
, a następnie próbuje rozpocząć ich śledzenie (patrz wiersze 490 - 506). Ponieważ B
jednostki mają teraz stan, Modified
prowadzi to do ich przyłączenia (nie dodania).