Optimizing Background Processing with HangFire in .NET

Overview

Scheduling and monitoring background tasks is challenging work. Every big application needs to implement background tasks to accomplish background work, such as data processing, email reminders, SMS queues, and email queues. Windows Service is the most typical approach to meet the necessity.

Hangfire is an open-source library that schedules and executes background jobs in .NET applications. You'll be able to create a simple background process inside the same application pool or thread without creating separate applications. Hangfire creates background jobs in persistence storage, like MS SQL Server, Redis, MongoDB, and others, that may prevent you from losing the job of recycling IIS pools or exception prevalence.

What is Hangfire?

Hangfire provides an easy-to-use framework for managing background jobs. It's designed to run tasks outside the request-response lifecycle, freeing your application to handle critical business logic without being blocked by time-consuming operations.

Hangfire lets you kick off method calls outside of the request processing pipeline in an effortless, but reliable way. These method invocations are performed in a background thread and called background jobs.

Key Features:

  • No Windows Service Required: Works as part of your .NET application.

  • Dashboard: Provides a real-time dashboard for monitoring jobs.

  • Scalable: Works with various storage providers like SQL Server, Redis, and others.

  • Supports ASP.NET Core and .NET Framework: A versatile choice for modern and legacy applications.

library consists of three main components: client, storage, and server. Here is a small diagram that describes the main processes in Hangfire:

Hangfire Workflow

Client

You can create any kind of background jobs using Hangfire:

Types of Hangfire Jobs

Hangfire supports four primary types of jobs:

1. Fire-and-Forget Jobs

These jobs run only once and are triggered immediately after creation. They're ideal for tasks like sending emails, processing user uploads, or logging activity.

Example:

public void SendEmail(string userEmail)
{
    BackgroundJob.Enqueue(() => EmailService.SendWelcomeEmail(userEmail));
}

2. Delayed Jobs

Delayed jobs are executed once, but only after a specified time delay. Use them for tasks that shouldn't run immediately, like sending follow-up notifications.

Example:

public void SendDelayedNotification(string userEmail)
{
    BackgroundJob.Schedule(() => NotificationService.SendReminderEmail(userEmail), TimeSpan.FromDays(1));
}

3. Recurring Jobs

Recurring jobs are executed repeatedly based on a schedule. This is perfect for tasks like database cleanup, sending daily reports, or running periodic maintenance.

Example:

    RecurringJob.AddOrUpdate("daily-reports", 
    () => ReportService.GenerateDailyReport(), Cron.Daily);

Hangfire uses CRON expressions to define recurring schedules.

4. Continuation Jobs

Continuation jobs run only after a parent job completes. This is useful for workflows where subsequent tasks depend on the success of the previous one.

Example:

var parentJobId = BackgroundJob.Enqueue(() => JobService.ProcessData());
BackgroundJob.ContinueWith(parentJobId, () => JobService.GenerateReport());

Storage

Hangfire keeps background jobs and other information that relates to the processing inside a persistent storage. Persistence helps background jobs to survive on application restarts, server reboots, etc. This is the main distinction between performing background jobs using CLR’s Thread Pool and Hangfire. Different storage backends are supported:

GlobalConfiguration.Configuration.UseSqlServerStorage("db_connection");

Setting Up Hangfire in ASP.NET Core

Here’s how you can integrate Hangfire into your ASP.NET Core application:

Step 1: Install Hangfire

Install Hangfire and its SQL Server storage provider (or another storage of your choice):

dotnet add package Hangfire
dotnet add package Hangfire.SqlServer

Step 2: Configure Hangfire

Add Hangfire to your Startup.cs or Program.cs file:

builder.Services.AddHangfire(config => 
    config.UseSqlServerStorage("YourConnectionString"));
builder.Services.AddHangfireServer();

Step 3: Use the Hangfire Dashboard

Enable the dashboard for real-time monitoring:

app.UseHangfireDashboard("/hangfire");

Best Practices for Hangfire Jobs

  1. Use Dependency Injection: Ensure your services used in jobs are registered in the DI container to avoid dependency issues.

     BackgroundJob.Enqueue<IEmailService>(service => service.SendWelcomeEmail(userEmail));
    
  2. Optimize Job Payloads: Pass minimal data to jobs, preferably IDs, to reduce overhead. Retrieve full data within the job itself.

  3. Monitor Jobs Regularly: Use the dashboard to keep track of failed jobs, execution times, and retries.

  4. Leverage Retry Policies: Hangfire automatically retries jobs on failure. Customize this behavior for specific jobs if needed.

  5. Secure the Dashboard: Protect the dashboard with authentication to prevent unauthorized access.

     app.UseHangfireDashboard("/hangfire", new DashboardOptions
     {
         Authorization = new[] { new MyAuthorizationFilter() }
     });
    

Common Use Cases for Hangfire

  1. Email Notifications: Send emails asynchronously without blocking the user experience.

  2. Data Processing: Offload heavy tasks like file uploads or image processing.

  3. Scheduled Reports: Automate daily, weekly, or monthly reports.

  4. Cache Updates: Refresh cached data periodically to keep it fresh.

  5. Database Maintenance: Clean up old records or archive logs.

Conclusion

Hangfire is a versatile library that can simplify background task management in your .NET applications. Its intuitive API, powerful dashboard, and scalability make it an excellent choice for developers. By understanding the different types of jobs and following best practices, you can unlock the full potential of Hangfire to streamline your workflows.