Reference · Backend · EF Core
EF Core: change tracking, Migration, N+1, AsNoTracking
Ringkasan satu halaman. Dari Lesson 0006.
DbContext dan change tracking [1]
- DbContext = sesi ke database, satu unit of work pendek (Scoped, satu per request; lihat Service Lifetime).
- Entity hasil query /
Add/Removedilacak. EF Core menyimpan snapshot, laluSaveChangesmembandingkan dan mengirim SQL. - Perubahan dilacak per property: UPDATE hanya kolom yang berubah. Tidak perlu
Update()untuk Entity yang dilacak.
| State | Arti | SaveChanges |
|---|---|---|
Added | baru (Add) | INSERT |
Unchanged | hasil query, belum diubah | — |
Modified | property diubah | UPDATE |
Deleted | ditandai Remove | DELETE |
Detached | tidak dilacak (mis. hasil AsNoTracking) | diabaikan |
Migration (code-first) [2]
dotnet ef migrations add AddProductSku # bandingkan model vs snapshot → file migration (di-commit)
dotnet ef database update # terapkan; dicatat di __EFMigrationsHistory
dotnet ef migrations script --idempotent # production: SQL script yang direview
- Code-first: model sumber kebenaran → Migration. Database-first: database sumber kebenaran → scaffold (reverse engineering). [3]
database updateuntuk lokal; production pakai SQL script atau bundle, karena migration bisa men-drop kolom. [4]- Padanan Node:
prisma migrate dev/prisma migrate deploy.
N+1 [5]
// ✗ lazy loading: 1 query Category + 1 query per Category
foreach (var c in await db.Categories.ToListAsync())
foreach (var p in c.Products) { ... }
// ✓ eager loading, satu query
await db.Categories.Include(c => c.Products).ToListAsync();
// ✓ projection ke DTO, hanya kolom yang perlu (juga tidak dilacak)
await db.Categories.Select(c => new CategoryResponse(c.Id, c.Name, c.Products.Select(p => p.Name).ToList())).ToListAsync();
- Deteksi: statement logging EF Core → SELECT yang sama berulang dengan parameter berbeda.
- Penyebab: lazy loading (
UseLazyLoadingProxies, opt-in) atau query di dalam loop. Microsoft menyarankan menghindari lazy loading. - Padanan Node: Prisma
includealih-alihawaitdi dalam loop.
AsNoTracking [6]
- Untuk query read-only (GET, laporan): tanpa snapshot dan change tracker, lebih cepat dan hemat memori.
- Jangan untuk data yang akan diubah: perubahan pada Entity no-tracking tidak disimpan oleh
SaveChanges, tanpa error. - No-tracking tidak melakukan identity resolution: baris yang sama bisa menjadi beberapa instance.
Jawaban 60–90 detik
Lihat log SQL dulu. N+1 = satu query awal + satu query per baris untuk relasinya, biasanya dari lazy loading di dalam loop.
Perbaiki dengan Include atau projection Select ke DTO dalam satu roundtrip. Untuk endpoint read-only pakai AsNoTracking
(tanpa snapshot/change tracker); endpoint yang mengubah data tetap tracking karena SaveChanges butuh change tracker.
Sumber: [1] Change Tracking · [2] Migrations Overview · [3] Managing Database Schemas · [4] Applying Migrations · [5] Efficient Querying · [6] Tracking vs. No-Tracking Queries