Introduction
Not every task in an application needs to be completed while a user is waiting for a response. Some operations, such as sending emails, generating reports, processing files, or cleaning up old data, can run in the background without affecting the user experience.
This is where .NET Worker Services are useful. They allow developers to build long-running background processes that operate independently of web requests. Worker Services are lightweight, scalable, and well-suited for cloud, on-premises, and containerized environments.
In this article, you'll learn what .NET Worker Services are, how they work, and how to build a simple background processing service.
What Are .NET Worker Services?
A .NET Worker Service is a background application designed to run continuously or execute scheduled tasks without requiring user interaction.
Unlike an ASP.NET Core Web API, which responds to HTTP requests, a Worker Service performs tasks in the background.
Common examples include:
Worker Services use the .NET Generic Host, which provides built-in support for dependency injection, logging, and configuration.
Why Use Worker Services?
Worker Services offer several advantages for background processing:
Keep long-running tasks separate from web applications
Improve application responsiveness
Support dependency injection
Integrate easily with cloud services
Work well in Docker containers
Can run as Windows Services or Linux daemons
Separating background tasks from your main application also makes the overall system easier to maintain and scale.
Creating a Worker Service
You can create a new Worker Service using the .NET CLI.
dotnet new worker -n BackgroundWorkerDemo
This command creates a project with the basic files needed for a background service.
The generated project already includes a sample worker class that runs continuously until the application stops.
Understanding the Worker Class
The main background logic is placed inside a class that inherits from BackgroundService.
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Background task is running.");
await Task.Delay(5000, stoppingToken);
}
}
}
In this example:
The worker runs continuously.
A log message is written every five seconds.
The service stops gracefully when the application shuts down.
Practical Example
Imagine you're building an e-commerce application.
When a customer places an order, several tasks need to happen:
Instead of making the customer wait for all these operations to complete, the API can save the order immediately and let a Worker Service handle the remaining tasks in the background.
This improves response times and provides a better user experience.
Using Dependency Injection
Like ASP.NET Core applications, Worker Services support dependency injection.
For example, you can inject a service into your worker.
public class Worker : BackgroundService
{
private readonly IEmailService _emailService;
public Worker(IEmailService emailService)
{
_emailService = emailService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Background processing logic
}
}
This approach keeps your code modular and makes it easier to test and maintain.
Common Use Cases
Worker Services are commonly used for:
These tasks typically don't require an immediate response to the user, making them ideal for background processing.
Error Handling
Background services should be designed to handle unexpected errors gracefully.
Consider these practices:
Catch exceptions inside processing loops.
Log detailed error information.
Retry temporary failures when appropriate.
Avoid stopping the entire service because of a single failed task.
Monitor the service to detect recurring issues.
Proper error handling helps keep your background processes reliable over time.
Best Practices
When building Worker Services, follow these recommendations:
Keep background tasks focused on a single responsibility.
Use asynchronous programming for I/O operations.
Respect the cancellation token to support graceful shutdowns.
Avoid blocking threads with long-running synchronous code.
Log important events and errors.
Use dependency injection for external services.
Store configuration values outside the code.
Monitor performance and resource usage.
These practices improve scalability, maintainability, and reliability.
Things to Consider
Before deploying a Worker Service, keep the following in mind:
Ensure tasks are idempotent whenever possible.
Protect shared resources when multiple workers are running.
Consider using queues for large workloads.
Test recovery scenarios after failures.
Monitor memory and CPU usage for long-running processes.
Planning for these scenarios helps create a stable and resilient background processing system.
Conclusion
.NET Worker Services provide a simple and effective way to build background processing applications. They allow developers to move time-consuming tasks out of the request pipeline, improving application performance and creating a better experience for users.
Whether you're processing messages, sending emails, generating reports, or synchronizing data, Worker Services offer a reliable foundation for long-running operations. By following best practices such as using dependency injection, handling errors gracefully, and respecting cancellation tokens, you can build scalable and maintainable background services that integrate seamlessly with modern .NET applications.