Reference · Backend · DI
Service Lifetime dan captive dependency
Ringkasan satu halaman. Dari Lesson 0004.
Tiga lifetime [1]
| Registrasi | Instance | Dispose | Pakai untuk | Padanan Node / NestJS |
|---|---|---|---|---|
AddTransient | baru setiap kali diminta | akhir scope | service ringan tanpa state | new X() · Scope.TRANSIENT |
AddScoped | satu per HTTP request | akhir request | DbContext, Repository, Service, data user per request | objek di req / res.locals · Scope.REQUEST |
AddSingleton | satu seumur aplikasi | app shutdown | cache, state mahal/global; harus thread-safe | instance di level module · Scope.DEFAULT |
Kenapa DbContext Scoped [4]
- DbContext = satu unit of work yang pendek. Di web app: satu request = satu unit of work.
AddDbContextmendaftarkannya Scoped secara default.- Satu request: Service dan Repository berbagi DbContext dan change tracker yang sama.
- Antar request: instance terpisah, karena DbContext tidak thread-safe.
Aturan aman [1]
Dependensi harus hidup sama lama atau lebih lama dari konsumennya.
| Konsumen ↓ menerima → | Transient | Scoped | Singleton |
|---|---|---|---|
| Transient / Scoped | aman | aman | aman |
| Singleton | hati-hati (ikut hidup selamanya) | captive dependency | aman |
| Middleware (constructor) | terima di parameter InvokeAsync, bukan constructor | aman | |
Captive dependency [2]
builder.Services.AddDbContext<AppDbContext>(...); // Scoped
builder.Services.AddSingleton<PriceCache>();
public class PriceCache(AppDbContext db) { } // ✗ DbContext ikut hidup selamanya
// Development: InvalidOperationException
// "Cannot consume scoped service 'AppDbContext' from singleton 'PriceCache'."
Deteksi dan perbaikan [3]
- Scope validation aktif default di Development: Scoped tidak boleh di-resolve dari root provider atau di-inject ke Singleton.
- Selalu aktif:
builder.Host.UseDefaultServiceProvider(o => o.ValidateScopes = true); - Perbaikan 1: turunkan lifetime konsumen (Singleton → Scoped).
- Perbaikan 2: inject
IServiceScopeFactory, laluusing var scope = f.CreateScope();dan ambil service dariscope.ServiceProvider(juga untukBackgroundService). - Beda NestJS: request scope "bubbles up" otomatis. .NET tidak; ia melempar exception.
Jawaban 60–90 detik
Transient: baru setiap diminta. Scoped: satu per request, di-dispose di akhir request; contoh DbContext karena satu request satu unit of work.
Singleton: satu seumur aplikasi, harus thread-safe. Captive dependency: service berumur panjang memegang dependensi berumur lebih pendek
(Singleton + DbContext), sehingga state bocor antar request dan DbContext dipakai paralel. Scope validation di Development melempar exception;
perbaiki dengan menurunkan lifetime atau IServiceScopeFactory.
Sumber: [1] Service lifetimes · [2] DI guidelines · [3] Scope validation · [4] DbContext lifetime · [5] NestJS injection scopes