Background Processing in .NET with Hangfire

By Bourbons
Last updated: 12.07.2017
.NET Hangfire Libraries

At cap hpi, we have a number of web applications that can trigger some form of background process. In this context, a background process is a function that can run for a potentially long time without requiring any intervention from a user.

To implement background processes, .NET developers have a range of options. Some of these are provided by the .NET framework, whilst others are provided in reusable code libraries written by the wider developer community. Some popular options in the .NET space are:

  1. Doing the processing "offline" by implementing a Windows Service or console application that processes items from a queue.
  2. In ASP.NET, developers can implement a framework-provided interface to register and run their long running tasks.
  3. Reusing existing .NET libraries such as Hangfire, Quartz.NET or FluentScheduler.

It is important to understand your requirements and assess the pros and cons of each option – so let’s dive a little deeper.

Custom Offline Processing

In an offline approach you typically have a loosely coupled architecture. The queue is a standalone component which can be abstracted away such that the application processing it doesn’t need to know anything about the underlying implementation. So the queue could be a simple database table or a more traditional message queuing system like MSMQ or RabbitMQ. The application processing the queue has full control of the processing and there is little risk of your process being terminated due to resource levels running low (a limitation of the next option).

A disadvantage of a custom approach is that you have more components to maintain, deploy and support across your environments. However, it can be argued that with modern deployment tools like Octopus Deploy you will only need to go through the pain once for setting up the deployments.

IRegisteredObject in ASP.NET

If your requirement is to run a background process in a web context, then ASP.NET gives you the option of implementing the System.Web.Hosting.IRegisteredObject interface. The interface defines just one method:

void Stop(bool immediate);

The intention is for the developer to write the background process in a class which implements IRegisteredObject, ensuring that their implementation of the Stop method handles any necessary clean-up work. The developer must then register the IRegisteredObject implementation with ASP.NET using the HostingEnvironment.RegisterObject method. When the runtime needs to terminate the background process (for example, if resources are running low in order to serve web requests), it can do so in a controlled manner using the implemented Stop method.

In fact, the runtime will call the Stop method twice. In the first instance, the runtime calls the Stop method and passes false for the immediate parameter. This gives the developer a chance to clean things up before the runtime calls Stop again with an immediate value of true. Not using the IRegisteredObject and simply spawning a new thread to run a background process in a web context can result in undesired behaviour, as explained here.

The limitation with using IRegisteredObject is that it doesn’t give the developer any assurance that their background process will complete. The runtime may, at any time, decide that it needs additional resources and therefore terminate your process albeit in a controlled manner.

Code Libraries

When looking for a solution to any problem, you don’t necessarily want to reinvent the wheel. If a mature code library already exists, then it’s usually a good idea to take advantage of it. Each library will have its pros and cons and choosing the right one can be difficult if it comes with little documentation. The .NET platform has a number of libraries that can be used for scheduling and managing long running processes and it is worth trying each of these before deciding which one to use.

At cap hpi, our teams have used Hangfire for scheduling and managing background processes. In the remainder of this post, we’ll look at why we use Hangfire, what it offers and how you can use its API to quickly start running and monitoring your background processes.

What is Hangfire? And why would I want to use it?

Hangfire is an open source .NET library which simplifies scheduling, running and monitoring background jobs in .NET and .NET Core applications. Our reasons for using Hangfire are:

  • It is available as a NuGet package and is therefore easy to integrate as part of build and deployment processes
  • It is well documented
  • It is easy to get started with (see the quick start guide)
  • It is actively used
  • The API is simple for new users
  • It has built-in mechanisms for retrying failed jobs
  • The built-in dashboard enables monitoring and management of background jobs (screenshots below)

Once installed, Hangfire gives you a number of options for running different types of background jobs. At the time of writing, Hangfire supports:

  • Fire-and-forget jobs – Run your process once and immediately
  • Delayed jobs – Run after a specified delay (using the TimeSpan class to specify the delay)
  • Recurring jobs – Run on a schedule (using the Cron class)
  • Continuations – Run after a parent job has completed

Additionally, the paid version of Hangfire supports batch jobs and batch continuations where you can run a group of jobs under a single encapsulating batch job.

The Hangfire API enables you to run each type of background job listed above. As you will see in the code examples below, a background job in Hangfire translates to a standard .NET programming language method.

One of the key benefits of using Hangfire is that it provides a pre-built dashboard to monitor your background processes (see Figure 1). The dashboard enables:

  • Viewing currently queued, scheduled or “in processing” background jobs
  • Viewing succeeded, failed and deleted jobs
  • Viewing detailed error information for jobs that have failed (exception and stack trace)
  • Scheduling recurring jobs using Cron syntax

 

The Hangfire Dashboard Home Page.

Figure 1: The Hangfire Dashboard Home Page

To start using Hangfire, install the NuGet package in your .NET project – which in Visual Studio is as simple as running the command "Install-Package Hangfire" in the Package Manager Console window. You can install Hangfire in ASP.NET web applications or non-web application projects. To get up and running with a default configuration follow the quick start guide on the Hangfire documentation site.

Once configured, Hangfire persists background job information in its own database. You have some flexibility in how it stores this data as the library supports a number of database implementations (SQL Server, Redis and MongoDb to mention a few). The library uses this database to track and run the jobs (see dashboard screenshot in Figure 2).

Job Management View

Figure 2: Job Management View

 

Writing Hangfire Jobs

In most use cases, to write a background process in Hangfire you do not need to implement any interface or extend any base or abstract class in order for the library to run your code. Your background process can be implemented as a static or instance class method and then passed into one of the Hangfire API methods as an action.

The API provides a convenient BackgroundJob class which supports a suite of static methods that enable background job execution. Each method supports a type of background job, so let’s take a look at each of these.

Fire-and-forget jobs can be executed using the BackgroundJob.Enqueue method, as shown below. These jobs are executed immediately.

var valueVehicleJobId = BackgroundJob.Enqueue(
	() => ValuationService.ValueVehicle(vehicleId)
);

Delayed jobs can be executed after a specified delay. Similar to the Enqueue method, you provide the job as the first argument to the BackgroundJob.Schedule method. Once the specified TimeSpan delay has elapsed, Hangfire will execute the job once.

var valueVehicleJobId = BackgroundJob.Schedule(
	() => ValuationService.ValueVehicle(vehicleId), 
	TimeSpan.FromDays(7)
);

Running a background job that is dependent on another job running first can be implemented using continuations. In the following example, the CleanUp method will only run after the specified parent job has completed:

BackgroundJob.ContinueWith(
	valueVehicleJobId, // parent job id
	() => CleanUp()
);

Recurring jobs can be executed using the RecurringJob class. The second argument to the RecurringJob.AddOrUpdate method is a string containing the schedule in Cron notation. Notice in the example we use the Cron class which contains static methods to return pre-defined Cron expressions.

RecurringJob.AddOrUpdate(
	() => ValuationService.ValueVehicle(vehicleId), 
	Cron.Monthly
);

Continuously running background processes are defined by implementing the IBackgroundProcess interface as shown below. Hangfire will continuously call your implementation of the Execute method. Notice that you are provided with a BackgroundProcessContext which has a useful Wait(TimeSpan) method that you can use to insert intervals between executions.

public class DoSomethingContinuously : IBackgroundProcess
{
	public void Execute(BackgroundProcessContext context)
	{
		// Do something
		// Use context.Wait(TimeSpan) to wait between executions
	}
}

These code examples show that the API behind Hangfire is intuitive and simple to use. Once you have set a background or recurring job you can monitor and manage its state in the dashboard.

Summary

Implementing background processes can be a time consuming task if you require the ability to monitor and manage them. As discussed at the beginning of this post, .NET developers have a number of options and each has its advantages and disadvantages. Before the advent of libraries like Hangfire, developers typically had to implement bespoke solutions – but managing the infrastructure around these solutions is an overhead that many teams may not want to support.

We discussed how Hangfire gives .NET developers a convenient option for running and managing background processes. Hangfire’s API makes it easy for developers to start running and managing background processes on the .NET platform. The pre-built dashboard helps overcome some of the maintenance overhead associated with writing your own bespoke solutions.