Interview frequency: …
Short answer
Inherit from BackgroundService and write the logic in ExecuteAsync(); Scoped services cannot be injected directly because the service is a Singleton, so create a scope manually with IServiceScopeFactory.
Explanation
BackgroundService is the standard way to run long-lived tasks, such as reading a queue or processing work on a schedule. Because a BackgroundService is a Singleton for the application's lifetime, injecting DbContext (which is Scoped) into its constructor throws InvalidOperationException. Instead, inject IServiceScopeFactory and create a scope with using (var scope = _scopeFactory.CreateScope()) inside each cycle.
Code example
public class QueueConsumer(IServiceScopeFactory scopeFactory) : BackgroundService {
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.ProcessPendingOrdersAsync(stoppingToken);
await Task.Delay(5000, stoppingToken);
}
}
}Common mistakes
- Ignoring the CancellationToken stoppingToken, preventing the application from shutting down gracefully when it receives a stop signal.