Background Job Scheduling in .NET using Hangfire
Tags: hangfire, csharp, dotnet, aspdotnet
Published: 2023-05-31 08:06:08

## Introduction
In this article we will look at how to use Hangfire to schedule background jobs in ASP.NET.


## Prerequisites
- Some knowledge of C# and ASP.NET
- SQL Server
- SQL Server Management Studio


## What is Hangfire?
Hangfire is an opensource framework that helps to efficiently create, process, and manage background tasks in an ASP.NET application.


## Why Hangfire
We might have tasks that we want to be performed outside the request processing pipeline, tasks that need to run after some time, or tasks that need to be run at specific intervals and do not require user interaction. For example, sending bulk emails, processing reports, searching for and retrying failed transactions, etc. Hangfire can help us with all these scenarios, and does this efficiently, as we will soon see.


## Hangfire Overview
Broadly speaking, the Hangfire library consists of 3 main components: client, storage and server.

![Diagram showing Hangfire's components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ey9o1zejyzn1f8sv8be.png)

### Client
The Hangfire client is used to create the background jobs. You can create various types of background jobs using Hangfire:

**Fire-and-Forget Jobs:** These are executed only once almost immediately after creation.

**Delayed Jobs:** These are tasks that are executed after a set timespan.

**Recurring Jobs:** These are jobs that are executed at defined intervals, eg hourly, daily, etc.

**Continuations Jobs:** These are jobs that are chained to other jobs and executed only when the parent job is done executing.

Background jobs in Hangfire can be created using regular static or instance method invocations. Here's an example of creating a fire-and-forget job using both techniques.

```
// 1)
var client = new BackgroundJobClient();
client.Enqueue(() => Console.WriteLine("Hello World"));

// 2)
BackgroundJob.Enqueue(() => Console.WriteLine("Hello World"));
```

### Storage
After a job is created using Hangfire, no data is kept in the application's memory. All details relating to the background job's processing, like types, method names, arguments, etc. are serialized and placed into a persistent storage. Persistence helps to keep your background jobs safe in the case of exceptions, application restarts or server reboots. In any of these instances, your background jobs will be retried automatically after restart.

Different storage backends are supported by Hangfire, eg SQL and Redis.

### Server
Background jobs are processed by the Hangfire server. It does this by querying the storage. The Hangfire server is basically a set of dedicated background threads that listen to the storage for new background jobs, and processes them by deserializing the types, method names and arguments.

## The Hangfire Dashboard
Another good thing about Hangfire is that it provides a graphical interface we can use to visualize and obtain information about our background jobs. We can see information relating to scheduled, succeeded or failed jobs on the Hangfire dashboard. We can also manually retry jobs from the dashboard.

![screenshot of the Hangfire dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lfjcip5fpgoogzrbx77k.png)


## Demo - Hangfire Installation and Configuration
We will create a simple ASP.NET Web Api project and demonstrate how to install and configure Hangfire. We will be using SQL Server as our Hangfire storage.
Our testing scenario will involve creating 3 endpoints to demonstrate 3 of the common jobs we can create using Hangfire.

a) A user signs up on our ecommerce app and we create a fire-and-forget job to send the user a welcome mail.
b) A user adds an item to their cart and we create a scheduled job to run after 5 minutes and send the user a reminder if the order has not been completed.
c) We call an endpoint to create a recurring job that runs hourly and checks for orders pending delivery.

### Step 1
Create an ASP.NET Web API project

![Screenshot of Web API project creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uv6rhrjkpuyjq9glquge.png)

### Step 2
Set the project and solution names

![Screenshot of Web API project creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsa97geg626oqluthj3g.png)

### Step 3
Select your target framework. I'm going with .NET 5 because I am running VS 19.

![Screenshot of Web API project creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ivi6cz54hmv1sf9r1j6.png)

### Step 4
Install the following NuGet packages using either the Package Manager Console or the UI window.
- Hangfire
- Hangfire.SqlServer
- Microsoft.SqlClient

![Screenshot of the Package Manager window on Visual Studio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9r6gdvnw7b7ev6kyxo35.png)

Note: We will also install Swashbuckle.AspNetCore to enable Swagger

### Step 5
Configure Hangfire in Startup.cs

```
using Hangfire;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HangfireDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddHangfire(options => options.UseSqlServerStorage("Server=.;Initial Catalog=HangfireDB; Integrated Security=true;TrustServerCertificate=True")));
            services.AddHangfireServer();
            services.AddSwaggerGen();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseSwagger();
            app.UseSwaggerUI();

            app.UseHttpsRedirection();

            app.UseHangfireDashboard();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

```
Here, in the ConfigureServices method we call the AddHangfire extension method and configure it to use SqlServer for storage by calling the UseSqlServerStorage extension method. We also call the AddHangfireServer extension method to enable Hangfire server.
In the Configure method, we add the UseHangfireDashboard middleware to the pipeline to enable the Hangfire dashboard. The default path for the Hangfire dashboard is https://localhost:port/hangfire.

### Step 6
Create the database. Open SSMS and create the database to be used as the Hangfire storage.

![Screenshot database creation on SSMS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62ptjyznb3frmfsxfifz.png)

### Step 7
Create HomeController and add endpoints.

```
using Hangfire;
using Microsoft.AspNetCore.Mvc;
using System;

namespace HangfireDemo.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class HomeController : ControllerBase
    {
        [HttpPost("register")]
        public IActionResult Register()
        {
            // register user

            // create fire-and-forget job to send welcome mail.
            BackgroundJob.Enqueue(() => SendWelcomeMail());

            return Ok();
        }

        [HttpPost("addtocart")]
        public IActionResult AddToCart()
        {
            // add item to user's cart

            // create scheduled job to send reminder mail if user has not completed order.
            BackgroundJob.Schedule(() => SendReminder(), TimeSpan.FromMinutes(5));

            return Ok();
        }

        [HttpGet("addrecurringjob")]
        public IActionResult AddRecurringJob()
        {
            RecurringJob.AddOrUpdate("Pending Deliveries Job", () => CheckPendingDeliveries(), Cron.Hourly);

            return Ok();
        }

        public static void SendWelcomeMail()
        {
            Console.WriteLine("Welcome mail has been sent to user");
        }

        public static void SendReminder()
        {
            bool completed = new Random().Next() % 2 == 0;

            if (completed)
            {
                Console.WriteLine("The user has already completed the order for cart item xxxx");
            }
            else
            {
                Console.WriteLine("A reminder has been sent to user for cart item xxxx");
            }
        }

        public static void CheckPendingDeliveries()
        {
            int number = new Random().Next(1, 10);

            Console.WriteLine($"There are {number} pending deliveries");
        }
    }
}

```

The BackgroundJob class provides static methods for creating fire-and-forget jobs, delayed jobs and continuation jobs.
The Enqueue method is used to create fire-and-forget jobs, and takes in the method call expression as a parameter. The Schedule method is used to create delayed jobs, and takes in the method call expression and a timespan indicating the delay.

The RecurringJob class provides static methods for creating and managing recurring jobs.
The AddOrUpdate method adds a new recurring job or updates an existing one. It takes in the job ID, the method call expression and the Cron expression for the interval.

When we run the application, we see the Swagger index page and our three endpoints. Calling the endpoints will create the different jobs we have set up.

![Screenshot of Swagger index page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbdw6xhqum35b9eudhd5.png)

When we navigate to the Hangfire dashboard, we can see information about the jobs.

![Screenshot of Hangfire dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7hc0lf9x98kuskrnrai.png)

![Screenshot of Hangfire dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ek3h665my8qngg4a2vx3.png)

As we can see, the Hangfire dashboard displays information about running and scheduled jobs, and also their success or failed status.

If we look at the HangfireDB we created earlier, we can see that a bunch of tables have been generated.

![Screenshot of Hangfire dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ficstst95hfp0viz5i4a.png)


##Summary
In this article we looked at how to use Hangfire to schedule background jobs in ASP.NET according to our requirements. In a follow up article, I will talk about using Hangfire with a Redis storage.
To learn more about Hangfire, you can visit the [official website](https://www.hangfire.io/).