5

I have ASP.NET Web API application. The application is using Unity as IoC container. The application is also using Hangfire and I am trying to configure Hangfire to use Unity.

So based on documentation i am using Hangfire.Unity which registers the unity container as a current job activator in Hangfire.

I have a class which has dependency on IBackgroundJobClient

 public class MyService
 {
   private MyDBContext _dbContext = null;
   private IBackgroundJobClient _backgroundJobClient = null;

   public MyService(MyDbContext dbContext, IBackgroundJobClient backgroundJobClient)
   {
     _dbContext = dbContext;
     _backgroundJobClient = backgroundJobClient;
   }
}

However even after configuring Hangfire.Unity it could not create & pass instance of BackgroundJobClient

So i had to register every dependency of BackgroundJobClient with unity container.

Unity Registration

public class UnityConfig
{

    private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
    {
        var container = new UnityContainer();
        RegisterTypes(container);
        return container;
    });


    public static IUnityContainer GetConfiguredContainer()
    {
        return container.Value;
    }

    public static void RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<MyDbContext>(new HierarchicalLifetimeManager(), new InjectionFactory(x => new MyDbContext()));       

        // register hangfire dependencies
        container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
        container.RegisterType<JobStorage, SqlServerStorage>(new InjectionConstructor("HangfireConnectionString"));
        container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
        container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
        container.RegisterType<IRecurringJobManager, RecurringJobManager>();
        container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
    }

}

OWIN Startup

    public class Startup
    {        
        public void Configuration(IAppBuilder app)
        {
            var container = UnityConfig.GetConfiguredContainer();


            Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString");
            Hangfire.GlobalConfiguration.Configuration.UseUnityActivator(container);

            // if i dont call UseSqlServerStorage() above then UseHangfireDashboard() method fails with exception
            //JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

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

            RecurringJob.AddOrUpdate<MyService>(x => x.Prepare(), Cron.MinuteInterval(10));
        }
    }

Code is working with such configuration. However i have questions:

Is this the correct way of configuring Unity with Hangfire?

Why do i need to invoke Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireConnectionString") in OWIN startup even though SqlServerStorage is already registered with Unity container as JobStorage?

If i dont invoke UseSqlServerStorage() method in OWIN startup then i get exception on app.UseHangfireDashboard() method.

JobStorage.Current property value has not been initialized. You must set it before using Hangfire Client or Server API.

LP13
  • 30,567
  • 53
  • 217
  • 400

1 Answers1

1

I believe there is a problem where you want to kick off Hangfire outside of the Unity ecosystem, but also want Unity to understand how to instantiate the appropriate Hangfire interfaces with the associated implementations. Since Hangfire itself doesn't use Unity, you will need to start up Hangfire with the appropriate configuration, such as the SQL Server connection string, and then use that configuration to inform Unity how to instantiate the Hangfire interfaces. I was able to solve this problem by setting the global Hangfire configuration for SQL and then use that same Hangfire static instance to set up Unity.

Here's example code where first you will see how I start the hangfire dashboard and server with a connection string:

public void Configuration(IAppBuilder app)
{ 
    var configuration = new Configuration(); // whatever this is for you

    GlobalConfiguration.Configuration.UseSqlServerStorage(
        configuration.GetConnectionString());

    GlobalConfiguration.Configuration.UseActivator(
        new HangfireContainerActivator(UnityConfig.GetConfiguredContainer()));

    app.UseHangfireDashboard("/hangfire", new DashboardOptions
    {
        Authorization = new[] {new HangfireAuthorizationFilter()}
    }); 
    app.UseHangfireServer();
}

As the second example, here's the configuration of Unity for Hangfire; notice how this code is using the static JobStorage Hangfire object to instantiate any requests for JobStorage.

    public static void RegisterHangfire(IUnityContainer container)
    {
        container.RegisterType<JobStorage>(new InjectionFactory(c => JobStorage.Current));
        container.RegisterType<IJobFilterProvider, JobFilterAttributeFilterProvider>(new InjectionConstructor(true));
        container.RegisterType<IBackgroundJobFactory, BackgroundJobFactory>();
        container.RegisterType<IRecurringJobManager, RecurringJobManager>();
        container.RegisterType<IBackgroundJobClient, BackgroundJobClient>();
        container.RegisterType<IBackgroundJobStateChanger, BackgroundJobStateChanger>();
    }

I believe this approach gives you the best of both worlds where you only set up your SQL Server connection once and you do it early to kick off Hangfire, but then you use that instance to tell Unity how to behave.

Jeremy Paskali
  • 140
  • 2
  • 8