Der Grund für den Fehler im bereitgestellten Code ist folgender.
Wenn Sie eine Entität Aaus der Datenbank erstellen, Swird ihre Eigenschaft mit einer Sammlung initialisiert, die zwei neue Datensätze enthält B. Idvon jeder dieser neuen BEntitäten ist gleich 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 {} };
Nach dem Ausführen der Codezeile enthält die var a = db.Set<A>().Single()Sammlung Svon Entitäten Akeine BEntitäten aus der Datenbank, da DbContext Dbkein verzögertes Laden verwendet wird und die Sammlung nicht explizit geladen wird S. Entität Aenthält nur neue BEntitäten, die während der Initialisierung der Sammlung erstellt wurden S.
Wenn Sie ein Framework IsModifed = truefür Sammlungsentitäten aufrufen S, wird versucht, diese beiden neuen Entitäten Bzur Änderungsverfolgung hinzuzufügen . Dies schlägt jedoch fehl, da beide neuen BEntitäten dasselbe haben 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;
An der Stapelverfolgung können Sie erkennen, dass das Entitätsframework versucht, BEntitäten zu folgenden Elementen hinzuzufügen 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)
Die Fehlermeldung besagt auch, dass eine Entität nicht verfolgt werden kann B, Id = 0da bereits eine andere BEntität mit derselben Entität Idverfolgt wird.
So lösen Sie dieses Problem.
Um dieses Problem zu beheben, sollten Sie Code löschen, der Bbeim Initialisieren der SSammlung Entitäten erstellt :
public ICollection<B> S { get; set; } = new List<B>();
Stattdessen sollten Sie die SSammlung an der Stelle füllen , an der sie Aerstellt wurde. Zum Beispiel:
db.Add(new A {S = {new B(), new B()}});
Wenn Sie kein verzögertes Laden verwenden, sollten Sie die SSammlung explizit laden , um ihre Elemente zur Änderungsverfolgung hinzuzufügen:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Warum werden die Instanzen von B nicht hinzugefügt, anstatt sie anzuhängen?
Kurz gesagt , sie werden angehängt, weil sie einen DetachedStatus haben.
Nach dem Ausführen der Codezeile
var a = db.Set<A>().Single();
erstellte Instanzen von Entitäten Bhaben Status Detached. Es kann mit dem nächsten Code überprüft werden:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Dann, wenn Sie einstellen
db.Entry(a).Collection(x => x.S).IsModified = true;
EF versucht, BEntitäten hinzuzufügen , um die Nachverfolgung zu ändern. Aus dem Quellcode von EFCore können Sie ersehen , dass dies uns zur Methode InternalEntityEntry.SetPropertyModified mit den nächsten Argumentwerten führt:
property- eine unserer BEinheiten,
changeState = true,
isModified = true,
isConceptualNull = false,
acceptChanges = true.
Diese Methode mit solchen Argumenten ändert den Status der Detached BEntitäten in Modifiedund versucht dann, die Verfolgung für sie zu starten (siehe Zeilen 490 - 506). Da BEntitäten jetzt den Status Modifiedhaben, werden sie angehängt (nicht hinzugefügt).