Introduction

In modern ASP.NET Core applications, especially those involving microservices, background processing is a key requirement. Whether it’s sending emails, processing Kafka messages, running cron-like jobs, or handling long-running workflows, background services are essential.

With the introduction of the Generic Host and robust improvements in .NET 6, 7, and 8, ASP.NET Core now provides powerful patterns through,

This article dives deep into how to design, build, and optimize background services for production.

Worker Services vs Hosted Services: What’s the Difference?

Feature IHostedService BackgroundService Worker Service Template
Interface-based Yes Yes (abstracts IHostedService) Yes (project template-based)
Long-running support Needs manual implementation Provides an infinite loop (ExecuteAsync) Yes
Best for Short-lived or event-based tasks Long-running background jobs Standalone background apps

Setting Up a Worker Service Project

You can create a worker service using.

dotnet new worker -n EmailBackgroundService

This creates a ready-to-run worker template using the Generic Host.

The Program.cs in .NET 8+ is minimal.

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddHostedService<EmailSenderWorker>();
    })
    .Build();

await host.RunAsync();

Creating a Custom BackgroundService

Here’s a simple example of a background worker.

public class EmailSenderWorker : BackgroundService
{
    private readonly ILogger<EmailSenderWorker> _logger;

    public EmailSenderWorker(ILogger<EmailSenderWorker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Sending queued emails at: {time}", DateTimeOffset.Now);
            await SendPendingEmails();
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }

    private Task SendPendingEmails()
    {
        // Implement actual email logic here
        return Task.CompletedTask;
    }
}

Advanced Hosted Services with IHostedService

When you need fine-grained control over start/stop behavior (e.g., for message queue consumers).

public class KafkaConsumerService : IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Connect to Kafka and begin consuming
        return Task.Run(() => ConsumeMessages(cancellationToken), cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // Cleanup, disconnect
        return Task.CompletedTask;
    }

    private void ConsumeMessages(CancellationToken cancellationToken)
    {
        // Poll Kafka and process messages
    }
}

Managing Multiple Background Services

You can register multiple services easily.

services.AddHostedService<EmailSenderWorker>();
services.AddHostedService<KafkaConsumerService>();
services.AddHostedService<DataSyncWorker>();

Each runs independently inside the host.

Handling Exceptions & Graceful Shutdown

To avoid crashing the entire app due to unhandled exceptions in workers.

try
{
    await SomeBackgroundTask();
}
catch (Exception ex)
{
    _logger.LogError(ex, "An error occurred.");
}

For graceful shutdown, ensure your workers respond to the CancellationToken from ExecuteAsync.

Dependency Injection in Background Services

Services are injected normally.

public class DataSyncWorker : BackgroundService
{
    private readonly IDataService _dataService;

    public DataSyncWorker(IDataService dataService)
    {
        _dataService = dataService;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _dataService.SyncExternalData();
    }
}

Scheduling and Cron-like Jobs

For scheduled tasks, consider,

Example using the Cronos package.

var cron = CronExpression.Parse("0 */5 * * * *");
var next = cron.GetNextOccurrence(DateTimeOffset.Now);

Logging, Metrics & Observability

Integrate with,

Production Tips

Conclusion

ASP.NET Core’s Hosted Services and Worker Services provide powerful primitives for executing background tasks in a clean, reliable, and scalable way.

Whether you're building message consumers, email dispatchers, or data processors, background services allow you to decouple, scale, and manage your application’s background logic like a pro.

Happy coding!