ASP.NET Core  

Background Jobs in ASP.NET Core: Hangfire vs Quartz.NET vs Hosted Services

Many business operations shouldn't run during an HTTP request. Sending emails, generating reports, processing invoices, synchronizing data, cleaning temporary files, and executing scheduled tasks are better handled in the background to improve application responsiveness and reliability.

ASP.NET Core provides multiple options for implementing background jobs, but choosing the right one depends on your application's requirements. While BackgroundService is ideal for lightweight workers, Hangfire and Quartz.NET offer advanced scheduling, retries, persistence, and monitoring capabilities.

Rather than comparing features at a high level, this article explains when to use each approach, how they differ, and which solution best fits common production scenarios.

Note: There is no single "best" background job framework. The right choice depends on whether you need simple background processing, recurring schedules, or enterprise-grade job orchestration.

Why Use Background Jobs?

Running long-running operations inside an API request can lead to:

  • Slow response times

  • Request timeouts

  • Poor user experience

  • Increased server resource usage

  • Failed operations if the request is cancelled

Moving these operations to background workers allows the API to respond immediately while the work continues independently.

Common Background Job Scenarios

Background processing is commonly used for:

  • Sending emails

  • Processing uploaded files

  • Generating invoices

  • Image resizing

  • Database cleanup

  • Scheduled reports

  • Cache refresh

  • Data synchronization

  • Queue processing

  • Notification delivery

These tasks typically don't require an immediate response to the user.

Choosing the Right Solution

The following comparison summarizes the strengths of each option.

FeatureHosted ServiceHangfireQuartz.NET
Built into ASP.NET Core
Cron schedulingLimited
Dashboard
Persistent jobs
Automatic retriesConfigurable
Distributed executionLimited
Learning curveLowMediumHigh

Each solution targets a different level of application complexity.

Option 1 – Hosted Services

Hosted Services are the simplest way to execute background work in ASP.NET Core.

Create a worker by inheriting from BackgroundService.

public class CleanupWorker : BackgroundService
{
    protected override async Task ExecuteAsync(
        CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("Cleaning files...");

            await Task.Delay(
                TimeSpan.FromHours(1),
                stoppingToken);
        }
    }
}

Register the service:

builder.Services.AddHostedService<CleanupWorker>();

Hosted Services work well for lightweight, continuously running tasks.

Option 2 – Hangfire

Hangfire simplifies background processing by storing jobs in persistent storage such as SQL Server or Redis.

Install Hangfire:

dotnet add package Hangfire.AspNetCore

dotnet add package Hangfire.SqlServer

Configure Hangfire:

builder.Services.AddHangfire(config =>
    config.UseSqlServerStorage(connectionString));

builder.Services.AddHangfireServer();

Create a background job:

BackgroundJob.Enqueue(() =>
    EmailService.SendWelcomeEmail());

Create a recurring job:

RecurringJob.AddOrUpdate(
    "daily-report",
    () => ReportService.Generate(),
    Cron.Daily);

Hangfire automatically retries failed jobs and provides a built-in web dashboard for monitoring execution.

Option 3 – Quartz.NET

Quartz.NET is designed for advanced scheduling scenarios.

Install Quartz:

dotnet add package Quartz

Create a scheduled job:

public class ReportJob : IJob
{
    public Task Execute(
        IJobExecutionContext context)
    {
        Console.WriteLine("Generating report...");

        return Task.CompletedTask;
    }
}

Quartz supports:

  • Cron expressions

  • Calendars

  • Job priorities

  • Misfire handling

  • Complex scheduling rules

  • Clustered execution

It is commonly used in enterprise systems with sophisticated scheduling requirements.

Feature Comparison

Different projects require different capabilities.

ScenarioRecommended Option
Simple background loopHosted Service
Email processingHangfire
Scheduled reportsHangfire
Enterprise schedulingQuartz.NET
Cron-based workflowsQuartz.NET
Lightweight workerHosted Service
Retry supportHangfire
Complex recurring schedulesQuartz.NET

Choosing the simplest solution that meets your requirements often results in easier maintenance.

Architecture Overview

flowchart LR

A[ASP.NET Core API]
B[Background Job]
C[(Database)]
D[Email Service]
E[Scheduler]

A --> B
B --> C
B --> D
E --> B

The API delegates long-running work to background processing while remaining responsive to incoming requests.

Performance Considerations

Keep these recommendations in mind:

  • Avoid CPU-intensive work inside request pipelines.

  • Use cancellation tokens where appropriate.

  • Configure retry policies carefully.

  • Prevent duplicate job execution.

  • Monitor job duration and failure rates.

  • Limit concurrent workers to avoid resource exhaustion.

Proper configuration improves both throughput and reliability.

Common Production Mistakes

ProblemRoot Cause
Duplicate jobsMissing idempotency
Memory leaksLong-running tasks holding resources
Lost jobsIn-memory scheduling without persistence
Thread starvationBlocking asynchronous code
Failed retriesIncorrect retry configuration
Overloaded workersUnlimited concurrency

Most production issues stem from poor job management rather than the framework itself.

Best Practices

  • Keep background jobs small and focused.

  • Store important jobs in persistent storage.

  • Log every failed execution.

  • Configure retry limits appropriately.

  • Monitor execution time and queue length.

  • Use dependency injection for job services.

  • Design jobs to be idempotent whenever possible.

Common Anti-Patterns

Avoid these implementation mistakes:

  • Running long operations directly inside controllers.

  • Creating fire-and-forget tasks using Task.Run().

  • Ignoring cancellation tokens.

  • Scheduling duplicate recurring jobs.

  • Performing heavy synchronous work inside background workers.

  • Assuming background jobs always complete successfully.

FAQ

Should I always use Hangfire instead of Hosted Services?

No. Hosted Services are sufficient for lightweight, continuously running tasks. Hangfire is more suitable when persistence, retries, and scheduling are required.

Is Quartz.NET better than Hangfire?

Not necessarily. Quartz.NET excels at advanced scheduling, while Hangfire focuses on developer productivity and job management.

Can Hosted Services process queues?

Yes. Hosted Services are commonly used to consume Azure Service Bus, RabbitMQ, or Kafka messages.

Can multiple Hangfire servers process the same jobs?

Yes. Hangfire supports distributed processing when configured with shared storage.

Conclusion

Background processing is essential for building responsive and scalable ASP.NET Core applications. Rather than executing long-running operations during HTTP requests, background jobs allow work to be processed asynchronously while improving user experience and application reliability.

Hosted Services, Hangfire, and Quartz.NET each solve different problems. By understanding their strengths, limitations, and ideal use cases, you can choose the right solution for your workload and build background processing systems that remain reliable, maintainable, and production-ready.