Der Grund für den Fehler im bereitgestellten Code ist folgender.
Wenn Sie eine Entität A
aus der Datenbank erstellen, S
wird ihre Eigenschaft mit einer Sammlung initialisiert, die zwei neue Datensätze enthält B
. Id
von jeder dieser neuen B
Entitä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 S
von Entitäten A
keine B
Entitäten aus der Datenbank, da DbContext Db
kein verzögertes Laden verwendet wird und die Sammlung nicht explizit geladen wird S
. Entität A
enthält nur neue B
Entitäten, die während der Initialisierung der Sammlung erstellt wurden S
.
Wenn Sie ein Framework IsModifed = true
für Sammlungsentitäten aufrufen S
, wird versucht, diese beiden neuen Entitäten B
zur Änderungsverfolgung hinzuzufügen . Dies schlägt jedoch fehl, da beide neuen B
Entitä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, B
Entitä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 = 0
da bereits eine andere B
Entität mit derselben Entität Id
verfolgt wird.
So lösen Sie dieses Problem.
Um dieses Problem zu beheben, sollten Sie Code löschen, der B
beim Initialisieren der S
Sammlung Entitäten erstellt :
public ICollection<B> S { get; set; } = new List<B>();
Stattdessen sollten Sie die S
Sammlung an der Stelle füllen , an der sie A
erstellt wurde. Zum Beispiel:
db.Add(new A {S = {new B(), new B()}});
Wenn Sie kein verzögertes Laden verwenden, sollten Sie die S
Sammlung 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 Detached
Status haben.
Nach dem Ausführen der Codezeile
var a = db.Set<A>().Single();
erstellte Instanzen von Entitäten B
haben 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, B
Entitä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 B
Einheiten,
changeState = true
,
isModified = true
,
isConceptualNull = false
,
acceptChanges = true
.
Diese Methode mit solchen Argumenten ändert den Status der Detached
B
Entitäten in Modified
und versucht dann, die Verfolgung für sie zu starten (siehe Zeilen 490 - 506). Da B
Entitäten jetzt den Status Modified
haben, werden sie angehängt (nicht hinzugefügt).