Introduction
ShopNest sends order confirmation emails and nightly reports without blocking HTTP requests — BackgroundService and Channel<T> queues process work off the request thread.
After this article you will
- Implement BackgroundService and IHostedService
- Use Channel for producer/consumer email queue
- Create scopes with IServiceScopeFactory for DbContext
- Compare Hangfire and Quartz.NET
- Monitor background work with health checks
Prerequisites
- Article 46 — Repository Pattern — Deep Dive
- ShopNest API, EF Core, and DI from prior modules
Concept deep-dive
public class EmailQueueService : BackgroundService
{
private readonly Channel<EmailMessage> _channel;
private readonly IServiceScopeFactory _scopes;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await foreach (var email in _channel.Reader.ReadAllAsync(stoppingToken))
{
using var scope = _scopes.CreateScope();
var sender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
await sender.SendAsync(email, stoppingToken);
}
}
public async Task EnqueueAsync(EmailMessage msg) =>
await _channel.Writer.WriteAsync(msg);
}
Scoped services: Never inject DbContext into singleton BackgroundService — create scope per message. Hangfire: persistent jobs with dashboard. Quartz: cron-style scheduling.
Hands-on — ShopNest Email Queue Processor
- EmailQueueService + Channel bounded capacity 1000.
- OrderPlacedEvent handler enqueues confirmation email.
- DailyReportBackgroundService runs at 2 AM UTC.
- Health check: queue depth under threshold.
Common errors & best practices
- Singleton holding scoped DbContext — ObjectDisposedException.
- No cancellation — app shutdown hangs on long work.
- Unbounded channel — memory exhaustion under spike.
Interview questions
Q: IHostedService vs BackgroundService?
A: BackgroundService abstracts ExecuteAsync loop — easier for long-running workers.
Q: Why Channel?
A: Thread-safe producer/consumer queue between HTTP and background worker.
Q: Hangfire when?
A: Need persistent retries, dashboard, scheduled jobs across restarts.
Summary
- BackgroundService runs outside HTTP pipeline
- Channel queues decouple request from email send
- IServiceScopeFactory resolves scoped DbContext per job
- Hangfire/Quartz for enterprise scheduling needs
Previous: Repository Pattern — Deep Dive
Next: Caching in ASP.NET Core
FAQ
Azure Functions instead?
For serverless burst workloads; BackgroundService fine in App Service.
Multiple instances?
Use distributed queue (Service Bus) so only one consumer processes each message.