ASP.NET Core background work: BackgroundService, PeriodicTimer and scoped DbContext

C# Language Interview Q&A November 13, 2026 csharp

A BackgroundService is a singleton, so scoped services (DbContext) must be resolved only from a new scope.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ======================================================================
// Q: How do you run background work in ASP.NET Core?
//
// A BackgroundService is a singleton, so scoped services (DbContext) must be
// resolved only from a new scope. For heavy or critical jobs: a separate worker
// / Azure Function / Hangfire, plus a message queue for reliability.
// ======================================================================

public class PriceSyncWorker(IServiceScopeFactory scopes, ILogger<PriceSyncWorker> log)
    : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
        while (await timer.WaitForNextTickAsync(stoppingToken))
        {
            try
            {
                await using var scope = scopes.CreateAsyncScope();   // new DbContext every time
                var sync = scope.ServiceProvider.GetRequiredService<PriceSync>();
                await sync.RunAsync(stoppingToken);
            }
            catch (Exception ex) when (ex is not OperationCanceledException)
            {
                log.LogError(ex, "Price sync failed");    // the worker keeps running
            }
        }
    }
}
builder.Services.AddHostedService<PriceSyncWorker>();