background-work-and-hosted-serviceslisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Background Work and Hosted Services
## The loop that must not die
`ExecuteAsync` is called once. An unhandled exception ends the service silently for the rest of the process lifetime (pre-.NET 8) or tears down the whole host (.NET 8+ default `BackgroundServiceExceptionBehavior.StopHost`). Neither is what a polling loop wants - it wants to log and continue:
```csharp
// WRONG: first transient DB blip permanently stops processing (or kills the app)
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessBatchAsync(stoppingToken);
await Task.Delay(_interval, stoppingToken);
}
}
// RIGHT: failure of one iteration is logged, backed off, and survived
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try { await ProcessBatchAsync(stoppingToken); }
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { break; }
catch (Exception ex) { _logger.LogError(ex, "Batch failed, retrying after backoff"); }
await Task.Delay(_interval, stoppingToken);
}
}
```
This loop is one of the three sanctioned homes of `catch (Exception)`. The `OperationCanceledException` filter matters: cancellation during shutdown exits cleanly instead of logging a spurious error.
## Scoped services in a singleton world
Hosted services are singletons; `DbCon