Today we are going to kick the tires of Hangfire, an open source solution for performing background tasks in .Net and .Net Core applications. In this example, we are going to use an ASP.Net MVC application, Entity Framework and Hangfire to add a background job consisting in adding one row to the database.
Hangfire
Some of the main features of Hangfire are:
- Easy to setup and easy to use: To start using Hangfire you don’t need to setup a Windows Service or Windows Scheduler. Everything you need is within Hangfire package.
- Reliable: Once a job is created to run in the background, you can be sure that Hangfire is going to process it.
- Efficient: You can use SQL Server, MSMQ or Redis to fetch your jobs.
- Persistent: You can store your background jobs in persistent storage like SQL Server (I am going to use this one), Redis, MongoDB (I wanted to use this one), PostgreSQL, etc.
- Distributed: Method calls and arguments are serialized (we should be able to see that on the DB). You can distribute Hangfire to different servers, synchronization is performed automatically.
- Self-Maintainable: Hangfire removes records automatically. You can see the default on the Hangfire Dashboard.
- Transparent: Very nice dashboard that displays your background processing and state of each job. Supports all logging frameworks.
- Extensible: Hangfire allows the addition of job filters.
- Open Source: Hangfire is open source (https://github.com/HangfireIO) and FREE for commercial use.
You can read more about Hangfire in their official page here: http://hangfire.io/
So, let’s start with a simple MVC web application displaying a list of Persons using Entity Framework.
Creating our MVC Web Application
Create a regular MVC application (Not CORE) in Visual Studio (2015 in my case). Cleanup some of the folders that you might not use (Not sure if a better option is the EMPTY template, I am going to try it one of these days, but for now that is what I do).
Entity Framework is already installed on the new application, so the first thing I do is to change the default connection string in web.config. I have a local SQL Developer 2016 installed in my PC, and this is the connection string I use to connect to my local database:
<add name="mySqlConnectionString" connectionString="Data Source={your-server-name}; Database=HangfireDb; Trusted_Connection=True; " providerName="System.Data.SqlClient" />
Creating the Model
Let’s create our first (and only) model: Person.cs. A list of these is going to show in our view. There is a folder “Models” created already, so we create the class there:
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace hangfire01.Models { public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } }
Creating the Context (Entity Framework)
Now, let’s create our Context class: HangfireDbContext.cs. First create a folder (“Database”) under the solution to hold our data layer files. Add and name a class for your context:
using System.Data.Entity; using hangfire01.Models; namespace hangfire01.Database { public class HangfireDbContext : DbContext { public HangfireDbContext() : base("mySqlConnectionString") { } public DbSet<Person> Persons { get; set; } } }
Creating the View and Controller
Next, we are going to code the view (Index.cshtml) and controller to display a list of Persons. We are going to add a couple of entities and check that everything is working fine. I am using Index.cshtml, but you can create your own view choosing a List view, the model Persons and the context we created to tie to EF.
@model IEnumerable<hangfire01.Models.Person> @{ ViewBag.Title = "Home"; } <h2>List</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.FirstName) </th> <th> @Html.DisplayNameFor(model => model.LastName) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.FirstName) </td> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.PersonId }) | @Html.ActionLink("Details", "Details", new { id = item.PersonId }) | @Html.ActionLink("Delete", "Delete", new { id = item.PersonId }) </td> </tr> } </table>
and the controller:
using System.Linq; using System.Web.Mvc; using hangfire01.Database; using hangfire01.Models; namespace hangfire01.Controllers { public class HomeController : Controller { private HangfireDbContext db = new HangfireDbContext(); public ActionResult Index() { // truncate People db.Database.ExecuteSqlCommand("TRUNCATE TABLE dbo.People"); // Add Person 1 AddPerson(1, "John", "Smith"); // Add Person 2 AddPerson(2, "Jane", "Doe"); // Add Person 3 AddPerson(3, "Michael", "Smith"); return View(db.Persons.ToList()); } public void AddPerson(int personId, string firstName, string lastName) { Person p = new Person {PersonId = personId, FirstName = firstName, LastName = lastName}; db.Persons.Add(p); db.SaveChanges(); } } }
And finally, we run the app and see the list of 3 people on the list.
Adding Hangfire to the Mix
The recommended way to add Hangfire is by using Nuget. So go to Tools è Nuget Package Manager è Manage Nuget Packages for Solution…
Browse for Hangfire. You should see a lot of results, you just need the one that says only “Hangfire by Sergey Odinokov”. Select your solution (or project) and install the package. The version I am using right now is 1.6.6 (Latest Stable).
Hangfire Setup
Once installed, we start by adding Hangfire to the OWIN’s Startup.cs:
using Hangfire; using Microsoft.Owin; using Owin; [assembly: OwinStartupAttribute(typeof(hangfire01.Startup))] namespace hangfire01 { public partial class Startup { public void Configuration(IAppBuilder app) { GlobalConfiguration.Configuration.UseSqlServerStorage("mySqlConnectionString"); app.UseHangfireDashboard(); app.UseHangfireServer(); } } }
Using Hangfire
We make a simple change to our controller, we are going to add the last two persons using Hangfire:
public ActionResult Index() { // truncate People db.Database.ExecuteSqlCommand("TRUNCATE TABLE dbo.People"); // Add Person 1 AddPerson(1, "John", "Smith"); // Add Person 2 and 3 using Hangfire var jobId = BackgroundJob.Enqueue(() => AddPerson(2, "Jane", "Doe")); BackgroundJob.ContinueWith(jobId, () => AddPerson(3, "Michael", "Smith")); return View(db.Persons.ToList()); }
So basically, we are adding 2 background jobs to the Hangfire queue. Person 3 will be processed after Person 2 is added (or the job completes).
Let’s run the app. Now, the view displays this list:
As you can see, now we can see only 2 people. This is the perfect scenario to see what Hangfire did (or continues to do) while watching the view.
Hangfire Dashboard
Like I mentioned before, Hangfire comes with a wonderful Dashboard that gives you all the information of what is happening inside. Let’s take a look:
First, our Dashboard shows all the job activity.
By clicking on Jobs, we can have an idea of what has been running on Hangfire:
We can dig deeper by clicking on each of the Job Ids:
So there it is… Our Person number 3 (“Michael”) shows as completed. It will be easy to add a way for the screen to refresh when the job is completed, but for this exercise this is enough. Maybe not quite yet…
Hangfire and SQL Server
Hangfire creates its own tables to persist and work through each of the background jobs we assigned to this engine. Let’s take a quick look at the tables created by Hangfire in SQL (Developer 2016):
And,if you want, you can run the queries to show the rows under the People table and run a querie to show all the jobs. You can see the serialization we were talking about before.
One more proof that Michael made it to the Db 🙂
Hope you like this article, please don’t forget to share this article, send a comment or visit an ad (if you find it interesting). Thanks.