Course contentsShow
← C#
Lesson 1520 of 1,90814. Background Work and MessagingFree lesson

Dependency Injection in Scoped Background Tasks

Resolve scoped services correctly within a single-threaded background execution context.

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.