Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hangfire RecurringJob with dependency injection

Is it possible to have Hangfire instantiate objects with the configure JobActivator when they're scheduled to run as a RecurringJob?

The signature of the method seems to force static only usages:

public static void AddOrUpdate<T>(
    string recurringJobId,
    Expression<Action<T>> methodCall,

I have several ideas on how i could "abuse" statics to back channel things around, but i feel like i might be missing something. Was there a design decision that hangfire only supports statics in chron jobs?

like image 993
Chris Marisic Avatar asked Oct 30 '15 21:10

Chris Marisic


2 Answers

Not sure when it was added, but I just did something similar to this in my current project and it works fine. The EmailBackgroundTask is a non-static class and that's a non-static method. The class has 4 dependencies injected via the Hangfire.Unity DI package.

RecurringJob.AddOrUpdate<EmailBackgroundTask>(x=>x.SendPeriodicEmail(),Cron.MinuteInterval(5));
like image 175
J Wynia Avatar answered Sep 22 '22 14:09

J Wynia


Quick answer is no, the default job activator only works on parameter-less constructors or static methods. I did something (in VB.net) quick and dirty to see if I could get it working and have shown it below.

By using "AddOrUpdate", you are telling Hangfire to create an instance of T and then access the method of T, so this signature works on instance members, not statics. if you use one of the other "AddOrUpdate" method signatures without the generic parameter, it will require a static.

Now the fun part: if type T doesn't have a parameter-less default constructor then it will fail using the default jobactivator as you said.

This is where you can now use a custom jobactivator to supply dependencies to the constructors of your tasks. If you create your own class inheriting from JobActivator then you can supply dependencies to your jobs.

Here is my VB code:

Imports Hangfire
Imports System.Reflection

Public Class JobActivationContainer
    Inherits JobActivator

    Private Property ParameterMap As Dictionary(Of Type, [Delegate])

    Private Function CompareParameterToMap(p As ParameterInfo) As Boolean
        Dim result = ParameterMap.ContainsKey(p.ParameterType)
        Return result
    End Function

    Public Overrides Function ActivateJob(jobType As Type) As Object
        Dim candidateCtor As Reflection.ConstructorInfo = Nothing
        'Loop through ctor's and find the most specific ctor where map has all types.
        jobType.
            GetConstructors.
            ToList.
            ForEach(
                Sub(i)
                    If i.GetParameters.ToList.
                        TrueForAll(AddressOf CompareParameterToMap) Then
                            If candidateCtor Is Nothing Then candidateCtor = i
                            If i IsNot candidateCtor AndAlso i.GetParameters.Count > candidateCtor.GetParameters.Count Then candidateCtor = i
                    End If
                End Sub
            )

            If candidateCtor Is Nothing Then
                'If the ctor is null, use default activator.
                Return MyBase.ActivateJob(jobType)
            Else
                'Create a list of the parameters in order and activate
                Dim ctorParameters As New List(Of Object)
                candidateCtor.GetParameters.ToList.ForEach(Sub(i)       ctorParameters.Add(ParameterMap(i.ParameterType).DynamicInvoke()))
            Return Activator.CreateInstance(jobType, ctorParameters.ToArray)
        End If
    End Function

    Public Sub RegisterDependency(Of T)(factory As Func(Of T))
        If Not ParameterMap.ContainsKey(GetType(T)) Then    ParameterMap.Add(GetType(T), factory)
    End Sub

    Public Sub New()
        ParameterMap = New Dictionary(Of Type, [Delegate])
    End Sub
End Class

I know this doesn't answer the question on how to "abuse" statics, but it does show how you could roll-your-own IoC container for hangfire, or use one of the already supported IoC's as per the manual: http://hangfirechinese.readthedocs.org/en/latest/background-methods/using-ioc-containers.html

Note: I plan to use a proper IoC, my code above was purely academic and would need a lot of work!

like image 20
Steve Avatar answered Sep 21 '22 14:09

Steve