Brandon Minnick has just released “From Zero to Hero: Asynchronous Programming in C#”, an in-depth course to help you master asynchronous programming using async/await in C#. You’ll never get async/await wrong again!
In software development, we often need to process long-running background tasks. These can include polling another service, monitoring tasks, processing queue messages, scheduling clean-up tasks, etc.
Since .NET 8, handling such scenarios is easier than ever. You can use .NET’s hosting model to create background services. Today, we will explore the BackgroundService class.
What is BackgroundService?
BackgroundService is an abstract base class in .NET that implements IHostedService, providing a straightforward way to create long-running background tasks. It’s designed specifically for scenarios where you need a task to run continuously throughout the application’s lifetime, such as:
- Processing messages from a queue
- Monitoring file system changes
- Performing periodic cleanup operations
- Handling scheduled tasks
- Processing data in the background
Creating Your First BackgroundService
Let’s create your first background service that does the task in time intervals – PeriodicBackgroundService.
public class PeriodicBackgroundService: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
PeriodicTimer time = new(TimeSpan.FromSeconds(1));
while (await time.WaitForNextTickAsync(stoppingToken))
{
Console.WriteLine($"Running background task: {DateTime.UtcNow}");
}
}
}
To use your PeriodicBackgroundService, you need to register it with the dependency injection container:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<PeriodicBackgroundService>();
var app = builder.Build();
app.Run();
When you start the application, you’ll see that PeriodicBackgroundService is also running.

Scoped Services
By default, Scoped Services are created per HTTP request in ASP.NET Core web applications.
But what about BackgroundService? When you inject the service registered as Scoped to the BackgroundService, you’ll get the error:
System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor ‘ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: BackgroundServiceExample.PeriodicBackgroundService’: Cannot consume scoped service ‘IScopedService’ from singleton ‘Microsoft.Extensions.Hosting.IHostedService’.)
That’s because the BackgroundService is registered as Singleton. I have already written about how to call Scoped Service in the Singleton.
To inject Scoped Service in the BackgroundService, you must manually create a scope. Also, you can create an asynchronous scope.
public class AnotherBackgroundService(IServiceProvider serviceProvider): BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
PeriodicTimer time = new(TimeSpan.FromSeconds(1));
while (await time.WaitForNextTickAsync(stoppingToken))
{
using IServiceScope scope = serviceProvider.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
scopedService.DoTask();
}
}
}
Best Practices
When implementing BackgroundService, keep these best practices in mind:
- Error Handling: Always wrap your main processing logic in try-catch blocks to prevent unhandled exceptions from crashing your service.
- Cancellation Token: Respect the cancellation token passed to ExecuteAsync to ensure your service can shut down gracefully.
- Resource Management: Properly dispose of resources using IDisposable when your service is stopped.
- Logging: Implement comprehensive logging to track the service’s operation and troubleshoot issues.
- Configuration: Use IOptions pattern for configurable values rather than hardcoding them.
Conclusion
Background services in .NET provide a powerful way to handle asynchronous tasks. By leveraging BackgroundService or IHostedService, you can implement long-running tasks efficiently. Following best practices like handling exceptions, using cancellation tokens, and managing scoped dependencies will ensure your background services are robust and reliable.