Not every task in an ASP.NET Core application should execute during an HTTP request. Operations such as sending emails, generating reports, processing files, or synchronizing data can increase response times and negatively impact the user experience if performed synchronously.

Background processing allows these long-running or non-critical tasks to execute independently of incoming requests. ASP.NET Core offers several approaches, each designed for different workloads and scalability requirements.

In this article, we'll compare the most common background processing options and discuss when to use each one in production applications.

Why Use Background Processing?

Imagine an e-commerce application where placing an order triggers several operations:

If all these tasks execute before returning a response, users experience unnecessary delays.

Instead, the API can immediately acknowledge the order while background workers process the remaining tasks asynchronously.

This improves application responsiveness and allows long-running operations to execute independently.

BackgroundService

BackgroundService is the built-in solution for running long-lived background tasks in ASP.NET Core.

Example:

public class Worker : BackgroundService
{
    protected override async Task ExecuteAsync(
        CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("Running background task...");

            await Task.Delay(5000, stoppingToken);
        }
    }
}

Register the service:

builder.Services.AddHostedService<Worker>();

Best Use Cases

It is lightweight and requires no external dependencies.

Hangfire

Hangfire is a popular library for persistent background jobs.

Unlike BackgroundService, Hangfire stores jobs in persistent storage such as SQL Server or Redis. Jobs survive application restarts and provide automatic retry capabilities.

Example:

BackgroundJob.Enqueue<IEmailService>(
    service => service.SendWelcomeEmail(userId));

Hangfire also provides a dashboard for monitoring:

Best Use Cases

Quartz.NET

Quartz.NET is a powerful scheduling library for recurring and calendar-based jobs.

Example scenarios include:

Quartz.NET supports advanced scheduling expressions that are difficult to implement using simple timers.

Best Use Cases

Azure Functions

Azure Functions provide serverless background execution based on events.

Functions can be triggered by:

Example timer trigger:

[Function("CleanupJob")]
public void Run(
    [TimerTrigger("0 */5 * * * *")] TimerInfo timer)
{
    Console.WriteLine("Cleanup executed.");
}

Azure automatically scales the execution based on workload.

Best Use Cases

Azure Service Bus

For enterprise applications, asynchronous messaging is often the preferred approach.

Instead of executing work immediately, an API publishes a message.

Client
   │
ASP.NET Core API
   │
Azure Service Bus
   │
Background Worker

The worker processes messages independently.

Benefits include:

This approach is widely used in microservices architectures.

Feature Comparison

FeatureBackgroundServiceHangfireQuartz.NETAzure FunctionsAzure Service Bus
Built into ASP.NET Core
Persistent jobs
Scheduling supportBasicGoodExcellentExcellentEvent-driven
Automatic retriesLimited
DashboardLimitedAzure PortalAzure Portal
Best forSimple background tasksBusiness jobsScheduled jobsServerless workloadsDistributed systems

Choosing the Right Solution

Choose BackgroundService when:

Choose Hangfire when:

Choose Quartz.NET when:

Choose Azure Functions when:

Choose Azure Service Bus when:

Best Practices

Common Mistakes

Running Long Tasks Inside Controllers

Controllers should return responses quickly. Executing lengthy operations during a request increases response time and reduces scalability.

Using BackgroundService for Critical Jobs

BackgroundService stores work in memory. If the application restarts unexpectedly, in-progress work may be lost. For critical workloads, prefer persistent job schedulers or message queues.

Ignoring Retry Logic

Network failures and temporary database issues are common. Background jobs should support retries with appropriate error handling.

Choosing a Complex Solution Too Early

Not every application requires Hangfire or Azure Service Bus. For simple periodic tasks, BackgroundService may be sufficient and easier to maintain.

Conclusion

Background processing is essential for building responsive and scalable ASP.NET Core applications. By moving long-running tasks outside the request pipeline, applications can improve user experience while handling work more efficiently.

The right solution depends on your requirements. BackgroundService is ideal for lightweight background tasks, Hangfire provides reliable job processing with persistence, Quartz.NET excels at advanced scheduling, Azure Functions enable serverless execution, and Azure Service Bus supports scalable, event-driven architectures.

Rather than choosing the most feature-rich option, select the approach that matches your application's complexity, reliability requirements, and deployment environment. A well-designed background processing strategy improves performance, enhances scalability, and makes your .NET applications easier to maintain as they grow.