.NETMedium
IHostedService vs BackgroundService in ASP.NET Core — when to pick which
Both are the supported way to run long-running background work inside an ASP.NET Core or generic-host process.
IHostedService | BackgroundService | |
|---|---|---|
| Type | Interface | Abstract class implementing IHostedService |
| You write | StartAsync + StopAsync | ExecuteAsync(CancellationToken) |
| Lifecycle | Manual — you control the loop | Framework runs ExecuteAsync once on startup, cancels on shutdown |
| Best for | Short startup hooks, periodic timers you manage | Continuous workers, queue consumers |
Pattern — a queue consumer (BackgroundService)
public class OrderProcessor : BackgroundService
{
private readonly Channel<Order> _queue;
private readonly ILogger<OrderProcessor> _log;
public OrderProcessor(Channel<Order> queue, ILogger<OrderProcessor> log)
{ _queue = queue; _log = log; }
protected override async Task ExecuteAsync(CancellationToken stop)
{
await foreach (var order in _queue.Reader.ReadAllAsync(stop))
{
try { await ProcessAsync(order, stop); }
catch (Exception ex) when (!stop.IsCancellationRequested)
{ _log.LogError(ex, "order {Id} failed", order.Id); }
}
}
}
Register:
builder.Services.AddHostedService<OrderProcessor>();
Pattern — startup hook (IHostedService)
public class WarmupCache : IHostedService
{
private readonly IMemoryCache _cache;
public WarmupCache(IMemoryCache cache) => _cache = cache;
public async Task StartAsync(CancellationToken ct)
=> await Preload(_cache, ct);
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
}
Production gotchas
StartAsyncblocks the host start. Do quick work, or kick offTask.Runfor fire-and-forget — but track the resulting task to await on shutdown.- Cancellation is cooperative. Pass the token through every
await; otherwise shutdown stalls 30 s waiting for graceful stop. - Scoped services —
BackgroundServiceis a singleton. Create scopes manually withIServiceScopeFactoryto use DbContext. - Multi-instance deployments — both run on every replica. Use a leader-election lock (Redis, Azure Blob lease) if exactly-once execution is required.