Background Services in ASP.NET Core
Implement long-running background 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.
Related Articles
Model Binding in ASP.NET Core
Understand how ASP.NET Core binds request data to action parameters.
Read More aspnetAPI Gateway Patterns
Explore common patterns and practices for building API gateways.
Read More aspnetAPI Versioning in ASP.NET Core
Learn about different strategies for API versioning in ASP.NET Core.
Read More