Often on a website build, you'll need a way to manage background tasks.  When you need to do things like scheduling orders, sending emails, refreshing data from an external feed, then you'll need a way to call code on a frequent basis.   In the old days in order to create a scheduled task, you would need a Windows service, however, as times moved and cloud hosting and web apps have become more popular, elastic deployments now mean we need new ways of handling this scheduling...  this is where hangfire comes in.  

Hangfire is a free and open-source framework that will allow you to easily perform background processing in .NET and .NET Core applications.  With Hangfire nNo Windows Service or separate process required.  Hangfire is backed by persistent storage so you can do IIS resets and not loose your data.  


If you need to use a scheduler, then I'm hoping I've convinced you that Hangfire is a good option.  In this tutorial, I'm going to cover everything you need to install Hangfire, lock it down so none of your web visitors can't access it AND make it apepar within the Umbraco backend.   So let's begin!

How To Install Hangfire

Getting Hangfire integrated within your Umbraco website a vanilla entity is easy.

You can install Hangfire via the NuGet Package Manager in Visual Studio.  On your web project, right-click and select 'Manage Nuget Packages',  type in 'Hangfire' and then add it to your project.

As Hangfire uses persistent storage, e.g a SQL database, we'll need to define a connection string to use with it.  As we're using Umbraco we can simply use the Umbraco one, that I'm assuming you've already set-up.

The next step to get Hangfire up and running is modifying your Startup.cs file and provide the necessary connection string and routing information.  As you're using Umbraco you may have to create the Start.cs yourself, so don't be surprised if you don't have one in your solution.

[assembly: OwinStartupAttribute(typeof(JonDJones.Website.Startup))]
namespace JonDJones.Core.MVC
{
    public partial class Startup : UmbracoDefaultOwinStartup
    {
        public override void Configuration(IAppBuilder app)
        {
            base.Configuration(app);

            var connectionString = Umbraco.Core.ApplicationContext.Current.DatabaseContext.ConnectionString;
            GlobalConfiguration.Configuration.UseSqlServerStorage(connectionString');

            var dashboardOptions = new DashboardOptions
            {
                Authorization = new[]
                  {
                    new UmbracoAuthorizationFilter()
                }
            };

            app.UseHangfireDashboard("/umbraco/backoffice/Plugins/hangfire", dashboardOptions);
            app.UseHangfireServer();
        }
    }
}

 Ok, so the important things to note from this snippet are. 

1. The assembly attribute.  The typeof() needs to point to your Startup file, this will be your websites assembly namespace.

2. Inherit from 'UmbracoDefaultOwinStartup'

3. Inherit from 'UmbracoDefaultOwinStartup'Umbraco.Core.ApplicationContext.Current.DatabaseContext.ConnectionString

4. WE're defining the route we will access hangfire within Umbraco.  To get a custom MVC view, or, in this case, a hangfire page to display within the Umbraco backend, it will need to be accessible within '/umbraco/backoffice/Plugins/'

5. We're adding 'UmbracoAuthorizationFilter' a custom class we'll define next.

Creating The IDashboardAuthorizationFilter

    public class UmbracoAuthorizationFilter : IDashboardAuthorizationFilter
    {
        public bool Authorize([NotNull] DashboardContext context)
        {
            var auth = new HttpContextWrapper(HttpContext.Current).GetUmbracoAuthTicket();
            if (auth == null)
            {
                return false;
            }
            return true;
        }

    }

I'm hoping the code for this is self-explanitory. It's checking if the current user has an Umbraco Authorization ticket set. If someone is logged into Umbraco they can then access it.

Web.config Changes

On the appSettings section of the web.config, make the following changes:

<add key="owin:appStartup" value="JonDJones.Website.Startup" /&gt
<add key="umbracoReservedPaths" value="~/umbraco,~/install/,~/hangfire" />

 Open you web.config (or app.settings.config) and make sure owin:appStartup is pointing to the right location.

Make Hangfire Display In Umbraco

  [Tree("content", "contentTree", "Event Handling")]
    public class ContentTree : BaseTree
    {
        public ContentTree(string application)
            : base(application)
        {
        }

        protected override void CreateRootNode(ref XmlTreeNode rootNode)
        {
            rootNode.NodeType = "example";
            rootNode.NodeID = "init";
            rootNode.Menu = new List { ActionRefresh.Instance };
        }

        public override void Render(ref XmlTree tree)
        {
 var hangfireNode = XmlTreeNode.Create(this);
            hangfireNode.NodeID = "3";
            hangfireNode.NodeType = string.Empty;
            hangfireNode.Text = "Hangfire";
            hangfireNode.Action = "#";
            hangfireNode.Action = "javascript:openPage('/umbraco/backoffice/Plugins/hangfire');";
            hangfireNode.Icon = "../../../App_Plugins/sass-icon-grey.png";
            hangfireNode.HasChildren = false;
            hangfireNode.Menu = new List();
            OnBeforeNodeRender(ref tree, ref hangfireNode, EventArgs.Empty);
            tree.Add(hangfireNode);
            OnAfterNodeRender(ref tree, ref hangfireNode, EventArgs.Empty);
        }

        public override void RenderJS(ref StringBuilder Javascript)
        {
            var adminJs = $"function openPage(url){{UmbClientMgr.contentFrame(url);}}";
            
            Javascript.Append(adminJs);
        }
    }
    

That's It!

In the example, you should see the hangfire option within your content section, called 'Event Handling' with a node called Hangfire.  In my website I've used a custom section, to make it easier.  If you want to know how to do this, you can read  'How To Create a custom section in Umbraco 7'

When you open the dashboard for the first time, Hangfire should create all of its database tables for you.  In your SQL database, you will see the following new tables, AggregatedCounter, Counter, Hash, Job, JobParameter, JobQueue, List, Schema, Server, Set, and State. 

Adding Jobs

Creating the code to create add a hangfire task can be done in two ways:

1. A one-off fire-and-forget request in Hangfire, using 
Enqueue()

BackgroundJob.Schedule(() => Console.WriteLine("One Off Task."), TimeSpan.FromMilliseconds(1000));

2. A regular reoccurring task, using the RecurringJob class, specifically with the AddOrUpdate() method

            
RecurringJob.AddOrUpdate(() => Console.WriteLine("Repeat Daily"), Cron.Daily());