Many business operations shouldn't execute during an HTTP request. Tasks such as sending emails, generating reports, synchronizing data with external systems, processing message queues, or cleaning temporary files can take several seconds—or even minutes—to complete. Executing these operations synchronously increases request latency, reduces application throughput, and negatively impacts user experience.
ASP.NET Core provides multiple approaches for running work in the background, each designed for different scenarios. Understanding when to use Hosted Services, Worker Services, and scheduled jobs helps build scalable, responsive, and maintainable applications.
In this article, we'll compare these background processing options, implement production-ready examples, and discuss best practices for choosing the right solution.
Understanding Background Processing
Why Run Tasks in the Background?
Background processing allows applications to perform long-running or recurring work independently of incoming HTTP requests.
Common examples include:
Moving these operations outside the request pipeline improves responsiveness and prevents users from waiting for non-essential tasks to finish.
Hosted Services
What Is a Hosted Service?
A Hosted Service runs alongside an ASP.NET Core application and starts automatically when the application starts.
ASP.NET Core provides the BackgroundService base class for implementing long-running background processes.
public sealed class EmailBackgroundService
: BackgroundService
{
private readonly ILogger<EmailBackgroundService> _logger;
public EmailBackgroundService(
ILogger<EmailBackgroundService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation(
"Checking email queue...");
await Task.Delay(
TimeSpan.FromSeconds(30),
stoppingToken);
}
}
}
Register the Hosted Service
builder.Services.AddHostedService<EmailBackgroundService>();
Why This Approach?
BackgroundService integrates directly with the ASP.NET Core hosting lifecycle.
The framework automatically starts the service when the application starts and signals it to stop gracefully during shutdown. Using the provided cancellation token ensures ongoing work can complete safely without abrupt termination.
Worker Services
What Is a Worker Service?
A Worker Service is a standalone .NET application designed exclusively for background processing.
Unlike Hosted Services, Worker Services don't expose HTTP endpoints unless you explicitly add them.
Typical use cases include:
The implementation closely resembles a Hosted Service because both rely on the Generic Host.
public sealed class InventoryWorker
: BackgroundService
{
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine(
"Synchronizing inventory...");
await Task.Delay(
TimeSpan.FromMinutes(5),
stoppingToken);
}
}
}
Why Choose a Worker Service?
Worker Services separate background workloads from web applications, allowing each component to scale independently.
For example, an e-commerce platform can scale its API based on incoming traffic while scaling inventory processors independently based on queue depth.
Scheduled Jobs
Some background work doesn't need to run continuously.
Examples include:
Daily reports
Weekly backups
Monthly billing
Database cleanup
Cache refresh
Instead of continuously polling for work, scheduled jobs execute at predefined intervals.
A common approach is to use a scheduling library that supports recurring tasks, retries, and monitoring.
The scheduling framework becomes responsible for determining when jobs execute, while your application focuses only on implementing the business logic.
Why Use Scheduled Jobs?
Scheduling avoids unnecessary CPU usage caused by constantly checking whether work exists.
It also provides better visibility into execution history, failures, and retry behavior.
End-to-End Implementation
Consider an online retail application.
Architecture:
Customer
│
▼
ASP.NET Core API
│
├────────► Order Database
│
├────────► Background Email Service
│
├────────► Inventory Worker
│
└────────► Scheduled Cleanup Job
Workflow:
A customer places an order.
The API immediately stores the order.
The HTTP response is returned without waiting for email delivery.
A Hosted Service processes the email queue.
A Worker Service synchronizes inventory with warehouse systems.
A scheduled job removes expired shopping carts every night.
Separating these responsibilities improves responsiveness while allowing each background process to evolve independently.
Choosing the Right Approach
| Requirement | Hosted Service | Worker Service | Scheduled Job |
|---|
| Runs with Web API | Yes | No | Yes |
| Standalone Process | No | Yes | Depends |
| Long-running Tasks | Yes | Yes | No |
| Recurring Schedule | Limited | Limited | Excellent |
| Independent Scaling | No | Yes | Depends |
| Best For | Background tasks inside web apps | Dedicated processing services | Time-based automation |
There is no universal solution. The correct choice depends on the application's architecture and operational requirements.
Best Practices
Keep HTTP requests short.
Move expensive operations to background processing.
Honor cancellation tokens.
Use dependency injection consistently.
Log background activity using structured logging.
Handle transient failures with retry policies.
Make background operations idempotent whenever possible.
Monitor execution time and failure rates.
Separate business logic from scheduling logic.
Common Mistakes
A common mistake is performing long-running work directly inside API controllers. This blocks request threads and reduces application scalability.
Another issue is ignoring cancellation tokens. Background services should always stop gracefully during application shutdown to avoid data corruption or incomplete processing.
Developers also frequently place too much business logic inside the background service itself. The service should orchestrate work, while reusable business logic remains in application services.
Testing and Validation
Background processing should be validated independently of the web application.
Recommended testing includes:
Unit testing business services
Integration testing background workflows
Queue processing validation
Scheduled job execution
Graceful shutdown testing
Failure and retry testing
Load testing
Performance monitoring
Testing should verify both successful execution and recovery from failures.
Performance Considerations
Background processing improves perceived application performance, but inefficient implementations can still consume excessive resources.
Consider these recommendations:
Avoid busy waiting and unnecessary polling.
Process work asynchronously.
Batch operations when appropriate.
Limit concurrent processing to protect downstream systems.
Monitor CPU, memory, and queue length.
Scale Worker Services independently when workloads increase.
Optimizing background processing helps maintain consistent application responsiveness under heavy load.
Security Considerations
Background services often have access to sensitive business data and infrastructure resources.
Follow these practices:
Use managed identities or secure service accounts.
Store secrets in secure configuration providers.
Encrypt sensitive data during transmission.
Validate data received from queues or external systems.
Restrict network permissions to required services only.
Log security-related events without exposing confidential information.
Apply least-privilege access to databases and messaging systems.
Security requirements remain the same whether code executes inside an HTTP request or a background process.
Troubleshooting
Background Service Never Starts
Verify that the service has been registered using AddHostedService() and that the application host starts successfully.
Scheduled Jobs Do Not Execute
Confirm that the scheduling framework is configured correctly and that recurring jobs have been registered during application startup.
Worker Service Stops Unexpectedly
Review application logs for unhandled exceptions. Background services should catch recoverable errors and continue processing where appropriate.
High CPU Usage
Check for tight loops that repeatedly poll for work without delays. Prefer event-driven or queue-based processing whenever possible.
Conclusion
Background processing is an essential part of building scalable ASP.NET Core applications. Hosted Services provide a simple way to execute background work alongside web applications, Worker Services are ideal for dedicated processing workloads, and scheduled jobs excel at recurring, time-based automation. Choosing the right approach—and separating background work from request processing—results in faster APIs, better scalability, and more maintainable production systems.