ASP.NET Core  

Background Processing in ASP.NET Core: Choosing the Right Approach

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:

  • Save the order

  • Send a confirmation email

  • Generate an invoice

  • Update inventory

  • Notify the warehouse

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

  • Periodic cleanup tasks

  • Cache refresh

  • Monitoring services

  • Scheduled synchronization

  • Health checks

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:

  • Running jobs

  • Failed jobs

  • Scheduled jobs

  • Retries

Best Use Cases

  • Email processing

  • Report generation

  • Scheduled notifications

  • Long-running business workflows

Quartz.NET

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

Example scenarios include:

  • Every day at midnight

  • Every Monday morning

  • First day of each month

  • Every five minutes

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

Best Use Cases

  • Scheduled reports

  • Database maintenance

  • Data synchronization

  • Batch processing

Azure Functions

Azure Functions provide serverless background execution based on events.

Functions can be triggered by:

  • HTTP requests

  • Timers

  • Azure Storage

  • Azure Service Bus

  • Event Grid

  • Blob uploads

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

  • Cloud-native applications

  • Event-driven systems

  • Scheduled cloud jobs

  • Lightweight automation

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:

  • Loose coupling

  • Improved reliability

  • Automatic retries

  • Load leveling

  • Better scalability

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:

  • Tasks run continuously.

  • No job persistence is required.

  • The application is relatively simple.

Choose Hangfire when:

  • Jobs must survive application restarts.

  • Automatic retries are important.

  • A monitoring dashboard is needed.

Choose Quartz.NET when:

  • Advanced scheduling is required.

  • Jobs follow complex calendars or cron expressions.

Choose Azure Functions when:

  • Building cloud-native applications.

  • Event-driven execution is preferred.

  • Automatic scaling is required.

Choose Azure Service Bus when:

  • Multiple services communicate asynchronously.

  • Reliability is critical.

  • High-volume message processing is expected.

Best Practices

  • Keep HTTP requests short and responsive.

  • Move long-running work to background services.

  • Design background jobs to be idempotent so retries don't produce duplicate results.

  • Use cancellation tokens to support graceful shutdown.

  • Log failures and monitor job execution.

  • Choose persistent job processing for critical business operations.

  • Use message queues for distributed applications instead of direct service calls.

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.