Alasan kesalahan dalam kode yang disediakan adalah sebagai berikut.
Ketika Anda membuat entitas A
dari database, propertinya S
diinisialisasi dengan koleksi yang berisi dua catatan baru B
. Id
dari masing-masing B
entitas baru ini sama dengan 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 {} };
Setelah mengeksekusi baris var a = db.Set<A>().Single()
pengumpulan kode S
entitas A
tidak mengandung B
entitas dari database, karena DbContext Db
tidak menggunakan lazy loading dan tidak ada pemuatan eksplisit koleksi S
. Entitas A
hanya berisi B
entitas baru yang dibuat selama inisialisasi koleksi S
.
Saat Anda menelepon IsModifed = true
untuk S
kerangka kerja entitas kumpulan mencoba menambahkan dua entites baru B
ke dalam pelacakan perubahan. Tetapi gagal karena kedua B
entitas baru memiliki hal yang sama 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;
Anda bisa melihat dari jejak tumpukan yang dicoba ditambahkan kerangka kerja B
entitas ke 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)
Dan pesan kesalahan juga memberitahu bahwa ia tidak dapat melacak B
entitas dengan Id = 0
karena B
entitas lain dengan yang sama Id
sudah dilacak.
Cara mengatasi masalah ini.
Untuk mengatasi masalah ini, Anda harus menghapus kode yang membuat B
entitas saat menginisialisasi S
koleksi:
public ICollection<B> S { get; set; } = new List<B>();
Sebaliknya Anda harus mengisi S
koleksi di tempat di mana A
dibuat. Sebagai contoh:
db.Add(new A {S = {new B(), new B()}});
Jika Anda tidak menggunakan lazy loading, Anda harus secara eksplisit memuat S
koleksi untuk menambahkan itemnya ke dalam pelacakan perubahan:
// Use eager loading, for example.
A a = db.Set<A>().Include(x => x.S).Single();
db.Entry(a).Collection(x => x.S).IsModified = true;
Mengapa itu tidak menambah dan bukannya melampirkan contoh B?
Singkatnya , mereka dilampirkan untuk ditambahkan karena mereka memiliki Detached
keadaan.
Setelah mengeksekusi baris kode
var a = db.Set<A>().Single();
instance entitas yang dibuat B
memiliki status Detached
. Itu dapat diverifikasi menggunakan kode selanjutnya:
Console.WriteLine(db.Entry(a.S[0]).State);
Console.WriteLine(db.Entry(a.S[1]).State);
Lalu ketika Anda mengatur
db.Entry(a).Collection(x => x.S).IsModified = true;
EF mencoba menambahkan B
entitas untuk mengubah pelacakan. Dari kode sumber EFCore, Anda dapat melihat bahwa ini mengarahkan kami ke metode InternalEntityEntry.SetPropertyModified dengan nilai argumen berikut:
property
- salah satu B
entitas kami ,
changeState = true
,
isModified = true
,
isConceptualNull = false
,
acceptChanges = true
.
Metode ini dengan argumen seperti itu mengubah keadaan dari Detached
B
entit ke Modified
, dan kemudian mencoba untuk memulai pelacakan untuk mereka (lihat baris 490 - 506). Karena B
entitas sekarang memiliki status Modified
ini menyebabkan mereka harus dilampirkan (tidak ditambahkan).