Skip to content

Background Tasks Using Hangfire Background Jobs

Edward edited this page Feb 22, 2022 · 2 revisions

Using Hangfire Background Jobs with Serenity

If you want to run background jobs with Serenity and would like to make use of Hangfire then this is a handy guide to get you started.

1. Add NuGet packages

If you're using .NET Core

Hangfire.AspNetCore
Hangfire.Core
Hangfire.SqlServer

If you're using ASP.NET

Hangfire.AspNet
Hangfire.Core
Hangfire.SqlServer
Microsoft.Owin
Microsoft.Owin.Host.SystemWeb
Owin

2. Add Permission Key

Create a new permission key in Modules/Administration/AdministrationPermissionKeys.cs

// .......
    [NestedPermissionKeys]
    [DisplayName("Administration")]
    public class PermissionKeys
    {
        // Add this key
        [Description("Background Jobs")]
        public const string BackgroundJob = "Administration:BackgroundJob";
// .......

3. Configure Startup.cs

If you're using .NET Core

Edit Startup.cs in Initialization

// .......
using Hangfire;
using Hangfire.SqlServer;
using Hangfire.Dashboard;
using Hangfire.Annotations;
// .......
        public void ConfigureServices(IServiceCollection services)
        {
// .......
            services.AddHangfire(configuration => configuration
                .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                .UseSimpleAssemblyNameTypeSerializer()
                .UseRecommendedSerializerSettings()
                // Reference the Default connection. If you want to add a new connection to 
                // Hangfire's database then remember to add this connection in your appsettings.json                
                .UseSqlServerStorage(Configuration.GetValue<string>("Data:Default:ConnectionString"), new SqlServerStorageOptions
                {
                    CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                    SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                    QueuePollInterval = TimeSpan.Zero,
                    UseRecommendedIsolationLevel = true,
                    UsePageLocksOnDequeue = true,
                    DisableGlobalLocks = true
                })
            );

            // Add the processing server as IHostedService
            services.AddHangfireServer();
        } // end of ConfigureServices

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery)
        {
// .......
            app.UseHangfireDashboard("/jobs", new DashboardOptions()
            {
                Authorization = new[] { new HangfireAuthorizeFilter() }
            });

            // Setting up some example jobs
            // BackgroundJob.Enqueue<Common.Jobs.SimpleJob>(job => job.Run());
            // RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), Cron.Hourly);
            // RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), "0 * * * *");
        } // end of Configure

        public class HangfireAuthorizeFilter : IDashboardAuthorizationFilter
        {
            public bool Authorize([NotNull] DashboardContext context)
            {
                return Authorization.HasPermission(Administration.PermissionKeys.BackgroundJob);
            }
        }

If you're using ASP.NET

Add a file Startup.cs in App_Start

using Hangfire;
using Hangfire.SqlServer;
using Microsoft.Owin;
using Owin;
using Serenity;
using Serenity.Data;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web;

[assembly: OwinStartup(typeof(YourAwesomeProject.Startup))]

namespace YourAwesomeProject
{
    public class Startup
    {
        private IEnumerable<IDisposable> GetHangfireServers()
        {
            GlobalConfiguration.Configuration
                .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
                .UseSimpleAssemblyNameTypeSerializer()
                .UseRecommendedSerializerSettings()
                // Reference the Default connection. If you want to add a new connection to 
                // Hangfire's database then remember to add this connection in your Web.config
                .UseSqlServerStorage(SqlConnections.GetConnectionString("Default").ConnectionString,
                    new SqlServerStorageOptions
                    {
                        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
                        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
                        QueuePollInterval = TimeSpan.Zero,
                        UseRecommendedIsolationLevel = true,
                        UsePageLocksOnDequeue = true,
                        DisableGlobalLocks = true
                    });

            yield return new BackgroundJobServer();
        }

        public void Configuration(IAppBuilder app)
        {
            var options = new DashboardOptions
            {
            	// You can add your own rules here, feedback welcome
                Authorization = new[] {
                    new AuthorizationFilter() {
                        Users = "admin"
                    }
                },
                AppPath = VirtualPathUtility.ToAbsolute("~")
            };

            app.UseHangfireAspNet(GetHangfireServers);
            app.UseHangfireDashboard("/jobs", options);

            // Setting up some example jobs
            // BackgroundJob.Enqueue<Common.Jobs.SimpleJob>(job => job.Run());
            // RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), Cron.Hourly);
            // RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), "0 * * * *");
        }
    }
}

4. Add a Navigation Item

In Modules/Administration, update AdministrationNavigation.cs

[assembly: NavigationLink(9000, "Administration/Background Jobs", url: "~/jobs", permission: YourAwesomeProject.Administration.PermissionKeys.BackgroundJob, icon: "fa-refresh", Target = "_blank")]

5. Create a Simple Job

Add a folder in Modules/Common called Jobs

Create a file called SimpleJob.cs, with the following contents:

using Serenity;
using System;

namespace YourAwesomeProject.Common.Jobs
{
    public class SimpleJob
    {
        // If you want to run SQL with a connection, add this
        // private readonly ISqlConnections Connections;
        // public SimpleJob(ISqlConnections connections) 
        // {
        //     this.Connections = connections ?? throw new ArgumentNullException(nameof(connections));
        // }

        public void Run()
        {
            new Exception("Hello Serenity from Hangfire!").Log();

            // using (var connection = Connections.NewFor<MyRow>())
            // {
            //     do stuff
            // }
        }
    }
}

6. Test it out

Uncomment the example jobs in your Startup.cs.

            // Setting up some example jobs
            BackgroundJob.Enqueue<Common.Jobs.SimpleJob>(job => job.Run());
            RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), Cron.Hourly);
            RecurringJob.AddOrUpdate<Common.Jobs.SimpleJob>(job => job.Run(), "0 * * * *");

Run your project, and access the jobs dashboard under your Administration navigation menu.

Clone this wiki locally