Im folgenden Beispielcode wird dabei die folgende Ausnahme angezeigt db.Entry(a).Collection(x => x.S).IsModified = true
:
System.InvalidOperationException: 'Die Instanz des Entitätstyps' B 'kann nicht verfolgt werden, da bereits eine andere Instanz mit dem Schlüsselwert' {Id: 0} 'verfolgt wird. Stellen Sie beim Anhängen vorhandener Entitäten sicher, dass nur eine Entitätsinstanz mit einem bestimmten Schlüsselwert angehängt ist.
Warum werden die Instanzen von B nicht hinzugefügt, anstatt sie anzuhängen?
Seltsamerweise wird in der Dokumentation für IsModified
nicht InvalidOperationException
als mögliche Ausnahme angegeben. Ungültige Dokumentation oder ein Fehler?
Ich weiß, dass dieser Code seltsam ist, aber ich habe ihn nur geschrieben, um zu verstehen, wie ef core in einigen seltsamen Fällen funktioniert. Was ich will, ist eine Erklärung, keine Umgehung.
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();
}
}
}
quelle
Antworten:
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ältB
.Id
von jeder dieser neuenB
Entitäten ist gleich0
.Nach dem Ausführen der Codezeile enthält die
var a = db.Set<A>().Single()
SammlungS
von EntitätenA
keineB
Entitäten aus der Datenbank, daDbContext Db
kein verzögertes Laden verwendet wird und die Sammlung nicht explizit geladen wirdS
. EntitätA
enthält nur neueB
Entitäten, die während der Initialisierung der Sammlung erstellt wurdenS
.Wenn Sie ein Framework
IsModifed = true
für Sammlungsentitäten aufrufenS
, wird versucht, diese beiden neuen EntitätenB
zur Änderungsverfolgung hinzuzufügen . Dies schlägt jedoch fehl, da beide neuenB
Entitäten dasselbe habenId = 0
:An der Stapelverfolgung können Sie erkennen, dass das Entitätsframework versucht,
B
Entitäten zu folgenden Elementen hinzuzufügenIdentityMap
:Die Fehlermeldung besagt auch, dass eine Entität nicht verfolgt werden kann
B
,Id = 0
da bereits eine andereB
Entität mit derselben EntitätId
verfolgt wird.So lösen Sie dieses Problem.
Um dieses Problem zu beheben, sollten Sie Code löschen, der
B
beim Initialisieren derS
Sammlung Entitäten erstellt :Stattdessen sollten Sie die
S
Sammlung an der Stelle füllen , an der sieA
erstellt wurde. Zum Beispiel:Wenn Sie kein verzögertes Laden verwenden, sollten Sie die
S
Sammlung explizit laden , um ihre Elemente zur Änderungsverfolgung hinzuzufügen:Kurz gesagt , sie werden angehängt, weil sie einen
Detached
Status haben.Nach dem Ausführen der Codezeile
erstellte Instanzen von Entitäten
B
haben StatusDetached
. Es kann mit dem nächsten Code überprüft werden:Dann, wenn Sie einstellen
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 unsererB
Einheiten,changeState = true
,isModified = true
,isConceptualNull = false
,acceptChanges = true
.Diese Methode mit solchen Argumenten ändert den Status der
Detached
B
Entitäten inModified
und versucht dann, die Verfolgung für sie zu starten (siehe Zeilen 490 - 506). DaB
Entitäten jetzt den StatusModified
haben, werden sie angehängt (nicht hinzugefügt).quelle
S
explizit geladen werden sollte, da der bereitgestellte Code kein verzögertes Laden verwendet. Natürlich hat EF zuvor erstellteB
Entitäten in der Datenbank gespeichert. Die CodezeileA a = db.Set<A>().Single()
lädt jedoch nur EntitätenA
ohne Entitäten in der SammlungS
. Zum Laden der Sammlung sollteS
eifriges Laden verwendet werden. Ich werde meine Antwort so ändern, dass sie explizit die Antwort auf die Frage "Warum wird nicht hinzugefügt, anstatt die Instanzen von B anzuhängen?" Einfügt.