Dalam kode contoh di bawah ini saya mendapatkan pengecualian berikut saat melakukan db.Entry(a).Collection(x => x.S).IsModified = true
:
System.InvalidOperationException: 'Instance dari tipe entitas' B 'tidak dapat dilacak karena instance lain dengan nilai kunci' {Id: 0} 'sudah dilacak. Saat melampirkan entitas yang ada, pastikan bahwa hanya satu instance entitas dengan nilai kunci tertentu yang dilampirkan.
Mengapa itu tidak menambah dan bukannya melampirkan contoh B?
Anehnya dokumentasi untuk IsModified
tidak menentukan InvalidOperationException
sebagai pengecualian yang mungkin. Dokumentasi atau bug tidak valid?
Saya tahu kode ini aneh, tetapi saya menulisnya hanya untuk memahami bagaimana ef core bekerja dalam beberapa kasus egde yang aneh. Yang saya inginkan adalah penjelasan, bukan pekerjaan.
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();
}
}
}
sumber
Jawaban:
Alasan kesalahan dalam kode yang disediakan adalah sebagai berikut.
Ketika Anda membuat entitas
A
dari database, propertinyaS
diinisialisasi dengan koleksi yang berisi dua catatan baruB
.Id
dari masing-masingB
entitas baru ini sama dengan0
.Setelah mengeksekusi baris
var a = db.Set<A>().Single()
pengumpulan kodeS
entitasA
tidak mengandungB
entitas dari database, karenaDbContext Db
tidak menggunakan lazy loading dan tidak ada pemuatan eksplisit koleksiS
. EntitasA
hanya berisiB
entitas baru yang dibuat selama inisialisasi koleksiS
.Saat Anda menelepon
IsModifed = true
untukS
kerangka kerja entitas kumpulan mencoba menambahkan dua entites baruB
ke dalam pelacakan perubahan. Tetapi gagal karena keduaB
entitas baru memiliki hal yang samaId = 0
:Anda bisa melihat dari jejak tumpukan yang dicoba ditambahkan kerangka kerja
B
entitas keIdentityMap
:Dan pesan kesalahan juga memberitahu bahwa ia tidak dapat melacak
B
entitas denganId = 0
karenaB
entitas lain dengan yang samaId
sudah dilacak.Cara mengatasi masalah ini.
Untuk mengatasi masalah ini, Anda harus menghapus kode yang membuat
B
entitas saat menginisialisasiS
koleksi:Sebaliknya Anda harus mengisi
S
koleksi di tempat di manaA
dibuat. Sebagai contoh:Jika Anda tidak menggunakan lazy loading, Anda harus secara eksplisit memuat
S
koleksi untuk menambahkan itemnya ke dalam pelacakan perubahan:Singkatnya , mereka dilampirkan untuk ditambahkan karena mereka memiliki
Detached
keadaan.Setelah mengeksekusi baris kode
instance entitas yang dibuat
B
memiliki statusDetached
. Itu dapat diverifikasi menggunakan kode selanjutnya:Lalu ketika Anda mengatur
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 satuB
entitas kami ,changeState = true
,isModified = true
,isConceptualNull = false
,acceptChanges = true
.Metode ini dengan argumen seperti itu mengubah keadaan dari
Detached
B
entit keModified
, dan kemudian mencoba untuk memulai pelacakan untuk mereka (lihat baris 490 - 506). KarenaB
entitas sekarang memiliki statusModified
ini menyebabkan mereka harus dilampirkan (tidak ditambahkan).sumber
S
harus dimuat secara eksplisit, karena kode yang disediakan tidak menggunakan lazy loading. Tentu saja, EF menyimpanB
entitas yang sebelumnya dibuat dalam database. Tetapi baris kodeA a = db.Set<A>().Single()
hanya memuat entitasA
tanpa entitas dalam koleksiS
. Untuk memuat koleksi,S
pemuatan yang cepat harus digunakan. Saya akan mengubah asnwer saya untuk secara eksplisit memasukkan jawaban untuk pertanyaan "Mengapa tidak menambahkan bukannya melampirkan contoh B?".