Ba cách đăng ký khác nhau về ai tạo instance và instance sống bao lâu.
AddDbContext— scoped, mỗi request một instance mới, dispose khi request kết thúc. Mặc định hợp lý cho web.AddDbContextPool— giữ một pool instance đã khởi tạo sẵn. Khi dispose, EF reset state (change tracker, config) và trả instance về pool thay vì bỏ đi. Tiết kiệm chi phí dựng context ở app có throughput cao.AddDbContextFactory— đăng ký một factory singleton sinh ra context mới theo yêu cầu; người gọi tự chịu trách nhiệm dispose.
Pooling có một ràng buộc quan trọng: vì instance được tái sử dụng, constructor không được nhận state theo request (tenant id, user id). Muốn state như vậy thì dùng AddDbContextPool với IDbContextFactory có tham số hoặc đơn giản là bỏ pooling.
Trong BackgroundService: hosted service là singleton và sống suốt vòng đời app, nên không được inject DbContext scoped trực tiếp — vừa là captive dependency, vừa chia sẻ một context cho nhiều vòng lặp song song. Dùng factory:
public class ExpireOrdersWorker(IDbContextFactory<AppDbContext> factory) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await using var db = await factory.CreateDbContextAsync(ct);
// one fresh context per iteration
await Task.Delay(TimeSpan.FromMinutes(5), ct);
}
}
}Cách khác tương đương là inject IServiceScopeFactory rồi CreateScope() mỗi vòng.
Hai kiểu đăng ký không loại trừ nhau: khai báo cả pool lẫn factory thì factory lấy context từ pool.