The Error
You hit this exception at runtime:
System.ObjectDisposedException: Cannot access a disposed object.
A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere.
Object name: 'AppDbContext'.
Quick Fix - 1 Minute
Your DbContext is Scoped but you're using it after the request ends. Inject IServiceScopeFactory instead.
Wrong - Crashes in background tasks:
public class EmailService
{
private readonly AppDbContext _db;
public EmailService(AppDbContext db) => _db = db;
public async Task SendAsync()
{
await Task.Delay(5000);
var user = await _db.Users.FirstAsync(); // CRASH - DbContext disposed
}
}
Right - Create a scope when you need it:
public class EmailService
{
private readonly IServiceScopeFactory _scopeFactory;
public EmailService(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
public async Task SendAsync()
{
await Task.Delay(5000);
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var user = await db.Users.FirstAsync(); // WORKS
}
}
Why This Happens
AppDbContext is registered as Scoped by default. A Scoped service is created per HTTP request and disposed when the request ends.
If you inject it into a Singleton, or use it in a Task.Run / background job that runs after the request, the context gets disposed before your code runs.
Common Scenarios
- BackgroundService / IHostedService: These are Singletons. Never inject
DbContextdirectly - Fire-and-forget from controller:
_ = _emailService.SendAsync()runs after request ends - Static methods: Trying to resolve
DbContextfrom a static context
Related Dev Fixes
- DI Scoped Service Error Fix
- Async Deadlock Fix
- NullReferenceException Fix
- Start C# Course →
- More dev fix solutions
Real-World Scenario: Hangfire Background Job
This is the #1 cause in production. Hangfire runs outside the HTTP request:
// Startup.cs - WRONG: Injecting DbContext directly
services.AddScoped<IEmailJob, EmailJob>(); // EmailJob takes AppDbContext
services.AddHangfire(config => config.UseSqlServerStorage(connStr));
// EmailJob.cs - CRASHES
public class EmailJob : IEmailJob {
private readonly AppDbContext _db;
public EmailJob(AppDbContext db) => _db = db; // Gets disposed immediately
public async Task Execute() {
var users = await _db.Users.ToListAsync(); // ObjectDisposedException
}
}
// FIX: Use IServiceScopeFactory
public class EmailJob : IEmailJob {
private readonly IServiceScopeFactory _scopeFactory;
public EmailJob(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
public async Task Execute() {
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var users = await db.Users.ToListAsync(); // Works
}
}
Why Hangfire fails: Jobs run minutes/hours after enqueue. The original HTTP request scope is long gone. You must create a new scope per job execution.
Related Fixes You Should Know
DbContext disposal issues trigger these errors next:
- Cannot Resolve Scoped Service From Root Provider - Root cause of this error. Happens when you inject DbContext into a Singleton service like IHostedService.
- DI Scoped Service Singleton Error - Same issue but caught at startup instead of runtime. ASP.NET 8 validates scopes on build.
- HostedService StartAsync Block - Using DbContext in StartAsync without a scope freezes your app on startup.
- BackgroundService CancellationToken Error - After fixing disposal, you still need to handle
stoppingTokenor jobs never shut down.
FAQ
Q: Can I make DbContext a Singleton to avoid disposal?
No. Never. DbContext is not thread-safe. Making it Singleton causes race conditions, data corruption, and memory leaks. Always keep it Scoped.
Q: Why does DbContext dispose work locally but fail in production?
Locally you test with 1 request. In prod, requests overlap. A slow background task keeps using a DbContext from request #1, but request #1 already ended and disposed it. Race condition.
Best Practice for.NET 8
1. Never inject DbContext into Singleton: Use IServiceScopeFactory
2. Create scope per unit of work: One scope = one job/email/operation
3. Use using: Ensures scope disposes even if exception thrown
// Template for any background work
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var service = scope.ServiceProvider.GetRequiredService<IEmailService>();
await service.SendAsync();
No comments yet. Be the first to share your thoughts!