Isaac.

aspnet

Background Services in ASP.NET Core

Implement long-running background services.

By Emem IsaacNovember 16, 20212 min read
#asp.net core#background services#hosted services#worker services
Share:

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

  1. Check cancellation: Respect StoppingToken
  2. Use scope for DB: Create scope per iteration
  3. Handle exceptions: Don't let service crash
  4. Log activity: Track what's happening
  5. 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.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours