EF Core: change tracking, Migration, N+1, dan AsNoTracking
Bagaimana DbContext tahu apa yang harus disimpan, dan apa yang Anda periksa saat query EF Core lambat
AsNoTracking?"
dalam 60–90 detik. Di akhir ada tugas malam: draf 4 cerita STAR.
1. DbContext dan change tracking
EF Core adalah O/RM. Model-nya "made up of entity classes and a context object that represents a session with the database." [1] Context itu adalah DbContext, yang di lesson 0004 Anda daftarkan sebagai Scoped karena ia "designed to represent a short-lived unit-of-work." [2]
"Each DbContext instance tracks changes made to entities. These tracked entities in turn drive the changes to the database when SaveChanges is called."
[2]
Setiap Entity yang dilacak punya EntityState:
| State | Artinya | Saat SaveChanges |
|---|---|---|
Added | baru, belum ada di database (db.Products.Add(p)) | INSERT |
Unchanged | hasil query, belum diubah. "All entities returned from queries are initially in this state." | — |
Modified | property diubah sejak di-query | UPDATE kolom yang berubah saja |
Deleted | ditandai db.Remove(p) | DELETE |
Detached | tidak dilacak DbContext | diabaikan |
Definisi state dan tabel aksinya: [2]
var p = await db.Products.FirstAsync(x => x.Id == 1); // Unchanged, snapshot disimpan
p.Price = 15000m; // Modified (property Price)
await db.SaveChangesAsync(); // UPDATE Products SET Price = @p0 WHERE Id = @p1
Tidak ada Update(). EF Core menyimpan snapshot saat Entity dimuat, dan saat SaveChanges "the application's instance is compared with the snapshot to discover the changes to be persisted."
[3]
"EF Core tracks changes at the property level", jadi UPDATE hanya berisi kolom yang berubah.
[2]
Padanan Node: Prisma tidak punya change tracker. Di sana Anda menulis prisma.product.update({ where, data }) secara eksplisit.
Di EF Core, Anda mengubah objek biasa lalu satu SaveChangesAsync menyimpan semua perubahan dalam unit of work itu.
Mirip save() di TypeORM / Mongoose, tapi untuk semua Entity sekaligus.
Latihan 1 — Apa yang dikirim SaveChanges?
Jawab tanpa scroll ke atas. Soal yang sama akan muncul lagi, diacak, di review campuran 0007.
Kuis di lesson ini adalah feedback cepat, bukan bukti penguasaan; yang dicatat hanya hasil di review campuran 0007.
2. Migration (code-first)
"If you want your EF Core model to be the source of truth, use Migrations." Kalau database yang menjadi sumber kebenaran, pakai reverse engineering (scaffold), yang biasa disebut database-first. [4] Product Catalog di ROADMAP memakai code-first.
dotnet ef migrations add AddProductSku # buat file migration di folder Migrations/
dotnet ef database update # terapkan ke database lokal
migrations add: "EF Core compares the current model against a snapshot of the old model to determine the differences, and generates migration source files", yang di-commit seperti kode biasa.database update: "EF Core records all applied migrations in a special history table", jadi hanya migration baru yang dijalankan. [5]- Production: cara di atas "is ideal for local development, but is less suitable for production environments."
[5]
Pakai SQL script (
dotnet ef migrations script --idempotent) yang bisa direview, karena "A migration may drop a column when the intent was to rename it." [6]
Padanan Node: prisma migrate dev (lokal) dan prisma migrate deploy (production), atau migration Knex. Folder Migrations/ + snapshot ≈ folder prisma/migrations/.
Latihan 2 — Migration
3. Masalah N+1
Ada tiga cara memuat relasi (navigation property): eager loading ("loaded from the database as part of the initial query"),
explicit loading, dan lazy loading ("transparently loaded from the database when the navigation property is accessed").
[7]
Lazy loading tidak aktif sendiri; ia perlu package Microsoft.EntityFrameworkCore.Proxies dan UseLazyLoadingProxies().
[8]
// lazy loading aktif, 50 Category
foreach (var c in await db.Categories.ToListAsync()) // 1 query
foreach (var p in c.Products) // +1 query PER Category
Console.WriteLine(p.Name); // total 1 + 50 = 51 query
"After the initial query loading all the blogs, we then have another query per blog, loading all its posts; this is sometimes called the N+1 problem, and it can cause very significant performance issues."
Cara melihatnya: nyalakan statement logging EF Core, lalu hitung SELECT yang berulang.
[3]
Solusinya: muat semua yang dibutuhkan dalam satu query.
// Eager loading
var cats = await db.Categories.Include(c => c.Products).ToListAsync();
// Lebih baik untuk API: projection langsung ke DTO (hanya kolom yang perlu)
var dto = await db.Categories
.Select(c => new CategoryResponse(c.Id, c.Name, c.Products.Select(p => p.Name).ToList()))
.ToListAsync();
Kalau datanya pasti dibutuhkan, "it is always better to use eager loading, so that EF can fetch all the required data in one roundtrip."
Projection dengan Select mengirim "only the needed columns."
[3]
Microsoft bahkan menyarankan: "Because lazy loading makes it extremely easy to inadvertently trigger the N+1 problem, it is recommended to avoid it."
[3]
N+1 tidak hanya dari lazy loading. Menulis await db.Categories.FindAsync(p.CategoryId) di dalam foreach juga N+1, sama seperti await query Prisma di dalam loop.
Prisma: "The n+1 problem occurs when looping through query results and performing one additional query per result", dan solusinya juga include.
[9]
Ingat juga lesson 0003: query baru dieksekusi saat ToListAsync (deferred execution), jadi Include/Select harus ditulis sebelum itu.
Latihan 3 — Temukan dan perbaiki N+1
4. AsNoTracking
Tracking ada harganya: EF Core memelihara dictionary Entity yang dilacak dan menyimpan snapshot setiap Entity. [3] Untuk data yang hanya dibaca, biaya itu sia-sia: "No-tracking queries are useful when the results are used in a read-only scenario. They're generally quicker to execute because there's no need to set up the change tracking information." [10]
// GET /api/products: hanya dibaca, langsung jadi DTO
var products = await db.Products.AsNoTracking().ToListAsync();
| Pakai tracking (default) | Pakai AsNoTracking |
|---|---|
PUT/PATCH/DELETE: query → ubah → SaveChangesAsync | GET daftar / detail yang hanya dikirim ke client |
| butuh satu instance per baris (identity resolution) | laporan / export besar yang read-only |
Jebakan interview: Entity dari AsNoTracking lalu diubah dan SaveChanges dipanggil → tidak ada yang disimpan, tanpa error.
Projection Select ke DTO juga tidak dilacak, karena "If the result set doesn't contain any entity types, then no tracking is done."
[10]
Padanan Node: Prisma selalu "no-tracking", karena hasilnya objek biasa.
Latihan 4 — Tracking atau tidak?
Latihan 5 — Ucapkan dengan suara keras
Pasang timer 90 detik dan jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.
"Endpoint EF Core Anda lambat. Apa itu masalah N+1, bagaimana mengatasinya, dan kapan Anda memakai AsNoTracking?"
Bandingkan dengan jawaban contoh dan checklist-nya
"Pertama saya nyalakan log SQL EF Core untuk melihat query yang benar-benar dikirim. Kalau ada satu SELECT untuk daftar Category lalu satu SELECT lagi per Category untuk Product-nya,
itu N+1: satu query awal ditambah N query untuk relasinya. Penyebab paling umum adalah lazy loading, karena relasi dimuat diam-diam saat property navigasi diakses di dalam loop.
Solusinya eager loading dengan Include, atau lebih baik lagi projection Select langsung ke DTO, supaya semuanya terambil dalam satu roundtrip dan hanya kolom yang perlu.
Lalu, untuk endpoint yang hanya membaca data, saya pakai AsNoTracking: EF Core tidak perlu menyimpan snapshot dan mendaftarkan Entity ke change tracker, jadi lebih cepat dan hemat memori.
Untuk endpoint yang mengubah data saya tetap memakai tracking query, karena SaveChanges butuh change tracker untuk tahu kolom mana yang di-UPDATE.
Padanannya di Prisma: include untuk menghindari N+1 di dalam loop."
Checklist: apakah Anda menyebut…
- langkah pertama: lihat log SQL EF Core untuk menghitung query?
- N+1 = satu query awal + satu query per baris untuk relasinya?
- penyebab umum: lazy loading / akses navigasi di dalam loop?
- solusi: eager loading
Includeatau projectionSelectke DTO dalam satu roundtrip? AsNoTrackinguntuk query read-only: tanpa snapshot / change tracker, lebih cepat?- endpoint yang mengubah data tetap tracking, karena
SaveChangesbutuh change tracker?
Lulus kalau minimal 4 dari 6. Review 0007 juga bisa menanyakan secara lisan "Bagaimana SaveChanges tahu apa yang harus di-UPDATE?"
dan "Apa itu Migration, code-first vs database-first?" dengan checklist yang sama gayanya.
Ringkasan untuk dicetak: Reference: EF Core · Istilah: GLOSSARY.md
Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "kapan Include justru lebih lambat (cartesian explosion)?" atau "apa beda AsNoTracking dan AsNoTrackingWithIdentityResolution?"
Tugas malam Hari 1 — draf 4 cerita STAR
Hari 1 selesai. Malam ini, sebelum tidur, tulis draf kasar 4 cerita dari pengalaman Node/React Anda (±30 menit, tidak perlu rapi). Lesson 0018 dipakai untuk memoles dan mengulangnya, bukan menyusun dari nol.
Format S-T-A-R (dari ROADMAP, Hari 3 Sesi 3):
| Isi | Panjang | |
|---|---|---|
| Situation | konteks: tim, produk, masalahnya | 1–2 kalimat |
| Task | tanggung jawab Anda di situ | 1 kalimat |
| Action | apa yang saya lakukan (bukan "kami"), langkah demi langkah | 3–5 kalimat |
| Result | hasilnya, pakai angka bila bisa (waktu, error rate, jumlah user) | 1–2 kalimat |
Empat tema (sesuai ROADMAP):
- Bug/insiden produksi tersulit yang Anda selesaikan.
- Refactor yang meningkatkan kualitas/maintainability (kaitkan ke SOLID/testing; Hari 2 akan memberi Anda istilahnya).
- Konflik/beda pendapat teknis dengan rekan dan cara menyelesaikannya.
- Mempelajari teknologi baru dengan cepat. Jembatannya: "persis seperti saya mempelajari .NET & Angular untuk posisi ini, ini mini project-nya."
Simpan di: star/draf-star.md (template empat cerita sudah tersedia).
Lesson 0018 dan mock interview akan membaca file itu. Kalau sempat, tambahkan juga draf jawaban "Kenapa pindah dari Node/React ke .NET/Angular?" di bagian bawah file yang sama.
Cukup poin-poin. Yang penting setiap cerita sudah punya S, T, A, R dan satu angka di Result. Besok pagi review 0007 dulu; ceritanya diendapkan sampai Hari 3.
Sumber: [1] Overview of Entity Framework Core · [2] Change Tracking in EF Core · [3] Efficient Querying · [4] Managing Database Schemas · [5] Migrations Overview · [6] Applying Migrations · [7] Loading Related Data · [8] Lazy Loading · [9] Prisma: Query optimization · [10] Tracking vs. No-Tracking Queries