Any code samples for scaling out Hangfire on .Net Core?

I’m able to successfully scale out Hangfire to multiple .Net Core boxes, running simple Console.Writeline() jobs. I seem to be doing something incorrectly with registration of Hangfire Client IoC containers, or perhaps my servers are not using those configured IoC containers correctly to resolve the concrete types in constructors. I get a mixture of ‘No Type registered’ and ‘No parameterless constructor’ exceptions when the server attempts to run the job.

Has anyone been able to find code samples for scaling out Hangfire servers and clients with the Asp.Net core default IoC container?

Currently using:
“Hangfire.AspNetCore”: “1.6.6” and “Hangfire.PostgreSql.NetCore”: “1.4.3”.

Thanks!

Edit: Startup.cs Files shown. I’m sure I’m doing something incorrectly, I just don’t know where.
Code for Startup.cs Hangfire Server:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Hangfire.PostgreSql;
    using Serilog;
    using Hangfire.Dashboard;

    namespace Hangfire.Web
    {
        public class Startup
        {
            public Startup(IHostingEnvironment env)
            {
                var builder = new ConfigurationBuilder()
                    .SetBasePath(env.ContentRootPath)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                    .AddEnvironmentVariables();
                Configuration = builder.Build();
            }

            public IConfigurationRoot Configuration { get; }

            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                // Add framework services.
                services.AddMvc();
                //exception thrown if this is not present
                services.AddHangfire(options => options
                    .UseStorage(new PostgreSqlStorage(Configuration["PostgresJobStoreConnectionString"]))
                    .UseColouredConsoleLogProvider()
                    //.UseActivator(AB.ISO.API.Controllers
                );

                services.AddSingleton<IConfiguration>(Configuration);


            }

            // 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, IApplicationLifetime appLifetime, IServiceScopeFactory scopeFactory)
            {
                loggerFactory.AddConsole(Configuration.GetSection("Logging"));
                loggerFactory.AddDebug();

                loggerFactory.AddSerilog();
                // Ensure any buffered events are sent at shutdown
                appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);

                app.UseMvc();
                //Hangfire:
                //GlobalConfiguration.Configuration
                //    .UseStorage(new PostgreSqlStorage(Configuration["PostgresJobStoreConnectionString"]));

                app.UseHangfireServer(
                    options: new BackgroundJobServerOptions()
                    {
                        ServerName = "AB's SERVER!",
                        Activator = new AB.ISO.API.IsoApiAspNetCoreJobActivator(scopeFactory)       //this AspNetCoreActivator is contained in the other project, and referenced here via Nuget             
                    },
                    storage: new PostgreSqlStorage(Configuration["PostgresJobStoreConnectionString"]));
                app.UseHangfireDashboard("/hangfire", new DashboardOptions()
                    {
                        Authorization = new List<IDashboardAuthorizationFilter>() { new NoAuthFilter() },
                        StatsPollingInterval = 5000 //can't seem to find the UoM on github - assuming ms
                    }
                );
            }
        }
    }

And the Startup code for Hangfire client:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using Hangfire;
    using Hangfire.PostgreSql;
    using AB.FileStore.Impl.MongoDB;
    using AB.FileStore.Interfaces;
    using Hangfire.AspNetCore;

    namespace AB.ISO.API
    {
        public class Startup
        {
            public Startup(IHostingEnvironment env)
            {
                var builder = new ConfigurationBuilder()
                    .SetBasePath(env.ContentRootPath)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                    .AddEnvironmentVariables();
                Configuration = builder.Build();
            }

            public IConfigurationRoot Configuration { get; }

            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                // Add framework services.
                services.AddMvc()
                    .AddControllersAsServices();


                services.AddTransient<IDbFileStorage<string>>((constructor) => new MongoDBFileStorageEngine(Configuration["FileStorage:FileStoreConnectionString"]));//file storage referenced externally via Nuget
                services.AddTransient<FileDownloadJobs>();
                services.AddSingleton<IConfiguration>(Configuration);
                services.AddSingleton<IServiceProvider>(services.BuildServiceProvider());
               // services.AddSingleton<JobActivator>(new IsoApiAspNetCoreJobActivator())
                //the lambda line is sketchy - doesn't seem top stick when the job itself runs (Global Configuration error)
                services.AddHangfire(config => config
                    .UseStorage(new PostgreSqlStorage(Configuration["Hangfire:JobStoreConnectionString"])));
              
                 
                


            }

            // 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, IServiceProvider serviceProvider, IServiceScopeFactory scopeFactory)
            {
                loggerFactory.AddConsole(Configuration.GetSection("Logging"));
                loggerFactory.AddDebug();

                app.UseMvc();
             
                app.UseHangfireDashboard();
                            
              
                GlobalConfiguration.Configuration.UseActivator(new IsoApiAspNetCoreJobActivator(scopeFactory));
                JobStorage.Current = new PostgreSqlStorage(Configuration["Hangfire:JobStoreConnectionString"]);
               

            }
        }
    }

Please post here your Startup.cs class, I need to investigate the configuration. But looks like you aren’t registering types that are used to perform background jobs.

Any update on this? I’d love to get a bigger demo working outside a “Hello World” console writeline job in asp net core so I can publish some material and help other developers to set this up.

Are you trying to create a background job on a basis of the FileDownloadJobs? Why it’s not registered on the server? In this case Hangfire doesn’t know how to create an instance of this class, and what parameters to pass during the initialization.

Correct - FileDownloadJobs works if I register it on the server - but I thought that was the whole point of AspNetCoreJobActivator? This is why I requested any code samples for IoC registration in Core.

If I copy paste the registrations from client to server, sure, it works.

But in the scenario where I have “Interface A” and 2 implementations, “B” and “C”. “B” is used by one Hangfire client and “C” is used by another, how are these supposed to be registered? Two Hangfire clients with different implementations of the same abstraction (registered by their own individual IoC containers) must have a way of passing in the IoC container to Hangfire to instruct it to use the appropriate implementation per client.

Does this make sense?

Hi,
Yeah you can hopefully do this, You shall try these options,

Hangsire let us to extend the Activatior please see

http://nance.io/dotnet/core/async/2016/08/19/long-running-tasks-in-dotnet-core-with-hangfire.html

And I prefer using Autofac as DI Container.

Idea is to extend the Activatior and pass in the needed Implementation based on your client.