Search code examples
dependency-injectioninversion-of-controlioc-containerhangfirehangfire-autofac

Hangfire Register IoC Dependency in Scope For Job at Run Time


Issue: most jobs are dependent on configuration dependency.

Ideal Solution: (copied here and at end just to save reading if you already know exactly how to do this)

I would like to

  • during job Q (using filter or any other options) save some data (object / json / string)
  • during job processing: intercept creation process of job and register dependency using stashed data to IoC container in scope for that job

Old Solution: for the custom "job scheduler" that I am replacing we take the following steps

  • before resolving the job processor we crate a lifetime scope and using job data register the dependency with data provided
  • then using "job type" resolve the job and process

Hack Solution: so this works for most but I still want to know how to accomplish what I am trying to do, by creating a "wrapper job" class that uses the same data to crate the same scope when running the job.

Ideal Solution: Ideally I can inject logic and

  • during save stash needed data (plenty of ways to do this)
  • during pre job processing access IoC to create the scope register this dependency

I have Tried / Looked Into Filter Attributes - works to save and restore data but have not found any access to IoC or lifetime scope here.

there were a few other options but they too were limited in "exposure" to the IoC container / scope

Ideal Solution: I would like to

  • during job Q (using filter or any other options) save some data (object / json / string)
  • during job processing: intercept creation process of job and register dependency using stashed data to IoC container in scope for that job

Solution

  • wow I am dense, so looking over the [Delegate Factories](Delegate Factories) documentation and taking a deeper dive into Implicit Relationship Types

    I realized I was making this a lot harder than it needed to be and what I was looking for is clearly documented here but I never looked close enough so here is a working sample

    note:

    1. not all dependencies are required at runtime, Autofac fills in everything not provided by the func
    2. you do no need to configure or register anything special to create the Func<>, just add it to the constructor of your dependent class

    Sample:

      public class ScratchPad
      {
          [Fact]
          public void WhenRunningThisThenItWorks()
          {
              var builder = new ContainerBuilder();
              builder.RegisterType<MyNeedyRuntimeClass>();
              builder.RegisterType<SomethingElseNeeded>();
              var container = builder.Build();
    
              var theFuncFactory = container.Resolve<Func<SomeRuntimeThing, MyNeedyRuntimeClass>>();
              var func = () => theFuncFactory.Invoke(new SomeRuntimeThing());
              func.Should().NotThrow("constructor need for `SomeRuntimeThing` should be passed in from Func and dependency for `SomethingElseNeeded` is handled from the service registration");
          }
      }
    

    here are the empty classes I used just for ref

      class MyNeedyRuntimeClass
      {
          public MyNeedyRuntimeClass(SomeRuntimeThing someRuntimeIdOrSomething, SomethingElseNeeded bar)
          {
              //....
          }
      }
    
      internal class SomeRuntimeThing
      {
      }
    
      internal class SomethingElseNeeded
      {
      }