Background Services in ASP.NET Core
Implement long-running background services.
By EMEPublished: February 20, 2025
asp.net corebackground serviceshosted servicesworker services
A Simple Analogy
Background services are like janitors working after hours. They run silently, performing maintenance while the main application serves users.
Why Background Services?
- Async work: Don't block user requests
- Scheduled tasks: Run at intervals
- Polling: Monitor for changes
- Cleanup: Maintenance operations
- Reliability: Integrated with host lifecycle
Hosted Service
public class DataSyncService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<DataSyncService> _logger;
public DataSyncService(IServiceProvider serviceProvider, ILogger<DataSyncService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_logger.LogInformation("Starting data sync");
using var scope = _serviceProvider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IDataService>();
await service.SyncAsync();
_logger.LogInformation("Data sync completed");
}
catch (Exception ex)
{
_logger.LogError(ex, "Data sync failed");
}
// Run every 5 minutes
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("DataSyncService is stopping");
return base.StopAsync(cancellationToken);
}
}
// Register
builder.Services.AddHostedService<DataSyncService>();
Windows Service
// Program.cs
builder.Host
.UseWindowsService(options => options.ServiceName = "MyAppService");
// Install
sc create MyAppService binPath= "C:\path\to\app.exe"
// Start
sc start MyAppService
Timer-Based Service
public class TimedCleanupService : IHostedService
{
private readonly ILogger<TimedCleanupService> _logger;
private Timer _timer;
public TimedCleanupService(ILogger<TimedCleanupService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Cleanup service started");
_timer = new Timer(DoCleanup, null, TimeSpan.Zero, TimeSpan.FromHours(1));
return Task.CompletedTask;
}
private void DoCleanup(object state)
{
_logger.LogInformation("Running cleanup");
// Cleanup logic here
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Dispose();
return Task.CompletedTask;
}
}
Scoped Service Access
public class NotificationService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _serviceProvider.CreateScope())
{
// Get scoped services
var emailService = scope.ServiceProvider
.GetRequiredService<IEmailService>();
var context = scope.ServiceProvider
.GetRequiredService<AppDbContext>();
var notifications = await context.Notifications
.Where(n => !n.Sent)
.ToListAsync(stoppingToken);
foreach (var notification in notifications)
{
await emailService.SendAsync(notification);
notification.Sent = true;
}
await context.SaveChangesAsync(stoppingToken);
}
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
}
Best Practices
- Check cancellation: Respect StoppingToken
- Use scope for DB: Create scope per iteration
- Handle exceptions: Don't let service crash
- Log activity: Track what's happening
- Graceful shutdown: Clean up resources
Related Concepts
- Worker services
- Hangfire background jobs
- Windows services
- System timers
Summary
Implement background services for long-running async operations. Use BackgroundService or IHostedService to integrate with application lifecycle.