The Error
You see this exception on app startup:
InvalidOperationException: Cannot resolve scoped service 'AppDbContext' from root provider.
Quick Fix - 30 Seconds
If you're running migrations or seeding data in Program.cs, wrap your code in a scope.
Wrong - Crashes:
var db = app.Services.GetService<AppDbContext>();
db.Database.Migrate(); // CRASH
Right - Works:
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate(); // WORKS
Why This Happens
.NET's DI container has 3 lifetimes:
- Singleton: Created once. Lives for the entire app. Example:
WebApplication.Services - Scoped: Created once per HTTP request. Example:
AppDbContext - Transient: Created every time you ask for it
The rule: A Singleton cannot hold a Scoped service. app.Services is the root Singleton. AppDbContext is Scoped. ASP.NET blocks this to prevent memory leaks and disposed contexts.
Real-World Scenario: BackgroundService Injecting DbContext
#1 cause of this error in production. BackgroundService is Singleton, DbContext is Scoped:
// WRONG: Crashes on startup
public class OrderProcessor : BackgroundService
{
private readonly AppDbContext _db; // Scoped service in Singleton
public OrderProcessor(AppDbContext db) // DI throws InvalidOperationException
{
_db = db;
}
}
// RIGHT: Use IServiceScopeFactory
public class OrderProcessor : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
public OrderProcessor(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using var scope = _scopeFactory.CreateScope(); // Dev Fix: New scope per loop
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var orders = await db.Orders.Where(o => o.Status == "Pending").ToListAsync(stoppingToken);
// Process orders...
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
}
Why this works: IServiceScopeFactory is Singleton. It creates a new scope for each work unit. DbContext is disposed after each loop, preventing memory leaks.
Related Fixes You Should Know
Root provider errors cascade into these:
- DI Scoped Service in Singleton Error - Same root cause but for your own services, not DbContext. Inject
IServiceScopeFactoryor redesign lifetime. - DbContext Was Disposed - You created the scope correctly, but held the DbContext reference after
usingblock ended. Don't store scoped services in fields. - BackgroundService CancellationToken - After fixing DI, you'll hit TaskCanceledException on shutdown. Pass
stoppingTokento EF queries. - Service Lifetime Mismatch - Injecting Transient into Singleton captures it forever. Use factory pattern or change lifetimes.
FAQ
Q: Can I just make my DbContext a Singleton?
No. DbContext is not thread-safe and tracks entities. Making it Singleton causes data corruption and memory leaks. Always use Scoped or create per-operation with IServiceScopeFactory.
Q: Why does app.Services.GetService work in Minimal APIs but crash in Program.cs?
Minimal API endpoints run inside a request scope. Program.cs after builder.Build() runs at root scope. Different scopes, different rules.
Step-by-Step: How to Debug
- Check the stack trace: Error usually points to
Program.csaftervar app = builder.Build() - Find
app.Services.GetService<T>(): This uses the root provider - Create a scope:
using var scope = app.Services.CreateScope(); - Resolve from scope:
scope.ServiceProvider.GetRequiredService<T>()
No comments yet. Be the first to share your thoughts!