In this lesson, you will learn how to bridge the gap between long-running BackgroundService tasks and short-lived scoped dependencies.
The Lifetime Mismatch
In .NET, a BackgroundService is a singleton. It lives for the entire duration of the application. However, many of your services—like DbContext or custom business logic classes—are registered as scoped. You cannot inject a scoped service directly into a singleton BackgroundService constructor because the scoped service would be captured forever, leading to memory leaks and invalid state.
The Solution: IServiceScopeFactory
To resolve scoped services within a background task, you must manually create a scope. By injecting IServiceScopeFactory into your background service, you can create a temporary "container" whenever you need to perform work.
Here is how you implement this pattern inside your ExecuteAsync method:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Create a scope for this specific iteration of work
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
// Resolve the scoped service from the provider
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
await DoWorkAsync(dbContext);
} // The scope is disposed here, cleaning up your scoped services
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
Why This Matters
When you use CreateScope(), the runtime generates a new IServiceProvider specifically for that block of code. When the using block finishes, the Dispose() method is called on the scope. This triggers the disposal of all scoped services created within that block, ensuring your database connections are closed and your memory remains stable.
If you try to resolve these services directly from the singleton BackgroundService, you violate the dependency injection contract, as the singleton will attempt to keep those scoped services alive long after their intended "request" or "unit of work" has ended. Always favor the IServiceScopeFactory to maintain a clean, per-task execution context.
Key Takeaway: Never inject scoped services into a singleton background service; instead, inject IServiceScopeFactory to manually create a scope that ensures proper disposal and service resolution for every unit of work.