Simple Background Jobs With Hangfire and ASP.NET Core

November 29, 2018 · 3 minute read

I recently found myself needing to schedule an automated task to run every night at midnight.

I’ve tried various solutions to this before (Quartz, going back about 10 years! and more recently Azure Functions).

But I needed to get this up and running quickly, it needed to be reliable and I didn’t want to throw something up that would be difficult for every subsequent developer to figure out.

So I opted for Hangfire and did a quick experiment to see how quickly I could get task scheduling up and running in an ASP.NET Core application.

It turned out to be pretty quick!

It always starts with a Nuget package

Assuming you’ve already got the ASP.NET Core application, you’ll need this package…

Install-Package HangFire

Then you can make a couple of small changes to startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // add this
    services.AddHangfire(
        x => x.UseSqlServerStorage("<conn-string-from-app-settings>")
    );
}

And…

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    // add these
    app.UseHangfireDashboard();
    app.UseHangfireServer();

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

Straight away, this gives you a handy dashboard (only available locally, by default) which you can get to by running your app, then heading to https://<your-site>/hangfire .

This is a useful way to see what tasks are scheduled, which have already happened plus track down any problems which may have occurred.

From here, you need to schedule whatever code you need scheduled!

In my case I declared a simple class (and interface)…

public interface IGenerateDailySalesReport {
    void ForAllCustomers();
}

public class GenerateDailySalesReport : IGenerateDailySalesReport {

    private AppContext _context;

    public GenerateDailySalesReport(AppContext context){
        _context = context;
    }

    public void ForAllCustomers(){
        // business logic and data persistence here...
    }
}

Hangfire hooks itself into Microsoft’s Dependency Injection framework, so the EF Core context here will be wired up automatically.

Now to register the report generator…

public void ConfigureServices(IServiceCollection services)
{
    // other code here...

    // added this...
    services.AddScoped<IGenerateDailySalesReport, GenerateDailySalesReport>();
}

And finally, schedule the ForAllCustomers method to be executed daily.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ 
    // existing code

    app.UseHangfireDashboard();
    app.UseHangfireServer();

    // added this
    RecurringJob.AddOrUpdate<IGenerateDailySalesReport>(
        salesReport => salesReport.ForAllCustomers(), Cron.Daily); 

    // existing code...
    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

And that’s it. Hangfire will retrieve the instance of our service and execute the specified method every day at midnight.

Hangfire tables

When you spin up your newly Hangfire-enabled service, it will attempt to create the tables it needs in the database.

If your connection string doesn’t have permission to create tables you’ll need to sort that out yourself and you can find the relevant scripts in the NuGet package.

Of course, with ASP.NET Core, NuGet packages are no longer in a packages folder in your application, instead you’ll find the script here…

%UserProfile%\.nuget\packages\hangfire.sqlserver\1.6.17\tools

Where 1.6.17 will be whichever version of Hangfire you’re using.

Check out the install.sql script in there for the exact migration you’ll need to create the tables yourself.

Now you can focus on your tasks

For me the big plus point of using Hangfire this way is that it’s straightforward to set up, to deploy (it’s just a web application) and extend as new requirements for scheduling tasks come in.

Azure Functions and the like can do this, but they bring their own learning curve and infrastructure with them. If you’d rather “keep it simple”, HangFire may well be your best option!

Join the Practical ASP.NET Newsletter

Ship better Blazor apps, faster. One practical tip every Tuesday.

I respect your email privacy. Unsubscribe with one click.