Background Tasks Using Hangfire Background Jobs - serenity-is/Serenity GitHub Wiki
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.
Hangfire.AspNetCore
Hangfire.Core
Hangfire.SqlServer
Hangfire.AspNet
Hangfire.Core
Hangfire.SqlServer
Microsoft.Owin
Microsoft.Owin.Host.SystemWeb
Owin
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";
// .......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);
            }
        }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 * * * *");
        }
    }
}In Modules/Administration, update AdministrationNavigation.cs
[assembly: NavigationLink(9000, "Administration/Background Jobs", url: "~/jobs", permission: YourAwesomeProject.Administration.PermissionKeys.BackgroundJob, icon: "fa-refresh", Target = "_blank")]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
            // }
        }
    }
}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.