Hangfire Jobs -Background processing with DotNet core
Published: 2023-01-28 15:04:32

## What's Hangfire?
Hangfire is an open-source library that allows you to kick-start the process in the background so you don't need a separate process or windows service for processing. It's also backed with persistent storage so you can pause, resume, stop and restart failed jobs at any point and it will keep track of activity in the persistent database, and everything is stored in storage which helps background jobs to survive **application restart, server reboot**, etc.

This library consists of three main components: `Client, Storage, and Server`. Below diagram will give you a high-level view of how these three components work together to complete the particular background process.

`Overview diagram:`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0s76twxcblchuslirhx.PNG)

---

## Types of background jobs
Hangfire supports several kinds of background tasks, below is the list of jobs that you can create using Hangfire. For more information about each type click [here](https://www.hangfire.io/overview.html#many-types-of-background-jobs).

- Fire-And-Forget
- Delayed
- Recurring
- Continuation
- Batches
- Batch Continuation
- Background process

---

In this article, we will be implementing the Hangfire server in the DotNet core application and SQL as persistent storage. So let's start with actual implementations. Here we are implementing recurring jobs, but you can follow the same project for other types of jobs too.

## Recurring Jobs:

- Create a new DotNet core application using Visual Studio:

Either you can create a new web application using VS or using a command prompt.

    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6os5hrseeeg8e0h9qua.PNG)

- Install Hangfire:

Hangfire is available as a NuGet package so you can install the below packages from the NuGet feed.

- Hangfire.Core
- Hangfire.SqlServer
- Hangfire.AspNetCore

```
dotnet add package Hangfire.Core
dotnet add package Hangfire.SqlServer
dotnet add package Hangfire.AspNetCore
```

Now we have all the required Hangfire packages so the next step is to prepare persistent storage, here I'm assuming that you have SQL Server installed and configured properly for use.

- Create a new Database:

As I mentioned earlier we will be using SQL Server as job storage, so connect to the local instance of the SQL server using the SSMS tool and create the database using the below command.

```
CREATE DATABASE [Hangfire]
GO
```
- Configure Hangfire Database in WebApp:

Open `appsettings.json` file and add a connection string.
 

```
{
  "ConnectionStrings": {
      "Hangfire": "Server=.;Database=Hangfire;Integrated Security=SSPI;"
    }
  .
  .
}
```

- Register services:

I have created the app using `DotNet Core 6` so I don't have `Startup.cs` file so I will be configuring hangfire in `Program.cs` file, if you have `DotNet 3.1` application you can use a similar configuration in `Startup.cs`. First, we need to import the required namespaces.

```
//...
using Hangfire;
using Hangfire.SqlServer;
```

By default DotNet core supports dependency injection and `Hangfire.AspNetCore` the NuGet package contains all the extension methods to register/configure `Hangfire Server`.

```
builder.Services.AddHangfire(config => config
                                .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                                .UseSimpleAssemblyNameTypeSerializer()
                                .UseRecommendedSerializerSettings()
                                .UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire"), new SqlServerStorageOptions
                                {
                                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                                    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                                    QueuePollInterval = TimeSpan.Zero,
                                    UseRecommendedIsolationLevel = true,
                                    DisableGlobalLocks = true
                                })
                            );

builder.Services.AddHangfireServer();
```
- Adding Dashboard UI:

Hangfire does have a super cool feature where it provides you a dashboard and you can enqueue, dequeue, re-queue, and many more operations you can perform from this UI. The following code snippet shows you how to add a dashboard UI in the web app, so you can use all features from the user-friendly UI itself and no need to remember complex commands.

```
app.UseEndpoints(endpoints =>
{
    endpoints.MapHangfireDashboard();
});
```

So finally your Program.cs file will look like the one below.

```
using Hangfire;
using Hangfire.SqlServer;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

//Configure Hangfire

builder.Services.AddHangfire(config => config
                                .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                                .UseSimpleAssemblyNameTypeSerializer()
                                .UseRecommendedSerializerSettings()
                                .UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire"), new SqlServerStorageOptions
                                {
                                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                                    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                                    QueuePollInterval = TimeSpan.Zero,
                                    UseRecommendedIsolationLevel = true,
                                    DisableGlobalLocks = true
                                })
                            );

builder.Services.AddHangfireServer();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

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

app.Run();
```

- Run the app:

Now we have everything configured to run the application so either you can press `F5 `or run the app using the command `dotnet run`.

- SQL Database Schema:

In `step 3` we have created separate DB for Hangfire jobs and in `step 4` configured the connection in `appsettings.json` file, so as soon as we run the application it will initialize the SQL table schema. You can find the list of tables in the below image.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ljzgs8yjif2mvf24cjb.png)

- Hangfire Dashboard:

Now our application is started, open the following URL to access the Hangfire Dashboard interface.

https://localhost:8057/hangfire

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmq70kcb3psd9683vowd.png)

By default, the hangfire server runs on port `8057 `and dashboard can be served on `/hangfire` endpoint path but you can configure this endpoint path while registering the dashboard in `Program/Startup.cs` file. The following snippet will configure a custom path for accessing Dashboard.

```
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHangfireDashboard("/dashboard");
});

```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5een7v3gruyodlw55wj.png)

---

Now we have everything configured to run `Hangfire Server` but still, we don't have any `recurring job` configured that's why on Recurring Jobs tab we are seeing `0` jobs on the dashboard, so let's configure one and see how we can queue and re-queue jobs from UI.

- Create a new project:

We can create new recurring jobs in the same project as well but just to keep everything isolated I have created new `class library` project within the same solution.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rsjdyl3cz2bd2g04ir6.PNG)

- Add UserMigration job:

Now create IUsersMigration interface and UsersMigration class which will have an actual implementation of the job.

`IUsersMigration.cs`

```
  public interface IUsersMigration
    {
        Task ExecuteAsync();
    }
```

`UsersMigration.cs` I have created this job for demo purposes so it contains basic implementation but you can implement your own logic, but I believe you got an idea about how we can create a new job.

```
  public class UsersMigration : IUsersMigration
    {
        public Task ExecuteAsync()
        {
            Console.WriteLine("Successfully completed!");

            return Task.CompletedTask;
        }
    }
```
So our entire solution structure will look like the below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9omgblrdohlf4z3qla5h.png)

- Register newly created Job:

In `Program.cs` file you have to register all the jobs with cronschedule. The following snippet will register recurring jobs as Never but you can change that schedule as per your requirement.

```
RecurringJob.AddOrUpdate<IUsersMigration>(x => x.ExecuteAsync(), Cron.Never);
```

Now your final `Program.cs` file will look as below.

```
using Hangfire;
using Hangfire.SqlServer;
using Workbench.Hangfire.Jobs.Migrations.Interfaces;
using Workbench.Hangfire.Jobs.Migrations.Jobs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

//Configure Hangfire

builder.Services.AddHangfire(config => config
                                .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                                .UseSimpleAssemblyNameTypeSerializer()
                                .UseRecommendedSerializerSettings()
                                .UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire"), new SqlServerStorageOptions
                                {
                                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                                    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                                    QueuePollInterval = TimeSpan.Zero,
                                    UseRecommendedIsolationLevel = true,
                                    DisableGlobalLocks = true
                                })
                            );

builder.Services.AddHangfireServer();

// Configure job's dependencies
builder.Services.AddScoped<IUsersMigration, UsersMigration>();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHangfireDashboard("/dashboard");
});

// Register job
RecurringJob.AddOrUpdate<IUsersMigration>(x => x.ExecuteAsync(), Cron.Never);

app.Run();
```

- Enqueue job:

You can select as many jobs as you wanted to enqueue and click on `Trigger now` button, as soon as you trigger the jobs it will maintain the information in persistent storage.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13evega28cq2kg4yzydg.png)

To check the status you can visit the `Jobs` tab.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ms9ftzayfetnpuqq63ez.PNG)

Here you can filter the list of jobs by their status so in the above image we are in `Succeeded` tab so all successful jobs will be listed here.

You can also check the status from SQL Server as well.

```
SELECT * FROM [HangFire].[Job];
```

---

### GitHub Repo [link](https://github.com/maheshmore2691/Workbench.Hangfire.Server).

---

## Conclusion

Hangfire is an open-source library and is free to use for commercial use. It also provides you with a dashboard so non-technical users can use the dashboard to queue, re-queue, and delete scheduled jobs from the server. It also supports authentication to view the dashboard so no need to worry about anonymous user access. Hangfire supports SQL, Redis, and In-Memory as persistent storage so jobs can survive application restarts and even machine reboots, etc.