Search code examples
c#.netdependency-injectionquartz.netsimple-injector

Constructor injection with Quartz.NET and Simple Injector


Currently I am writing a service using Quartz.NET to schedule the running of it.

I was wondering if anyone has any experience of using constructor injection with Quartz.NET and Simple Injector.

Below is essentially what I wish to achieve

public class JobImplementation: IJob
{
    private readonly IInjectedClass injectedClass;

    public JobImplementation(IInjectedClass _injectedClass)
    {
         injectedClass = _injectedClass
    }

    public void Execute(IJobExecutionContext _context)
    {
        //Job code
    }

Solution

  • According to this blog post, you would need to implement a custom IJobFactory, like this:

    public class SimpleInjectorJobFactory : IJobFactory
    {
        private readonly Container container;
        private readonly Dictionary<Type, InstanceProducer> jobProducers;
    
        public SimpleInjectorJobFactory(
            Container container, params Assembly[] assemblies)
        {
            this.container = container;
    
            // By creating producers, jobs can be decorated.
            var transient = Lifestyle.Transient;
            this.jobProducers =
                container.GetTypesToRegister(typeof(IJob), assemblies).ToDictionary(
                    type => type,
                    type => transient.CreateProducer(typeof(IJob), type, container));
        }
    
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler _)
        {
            var jobProducer = this.jobProducers[bundle.JobDetail.JobType];
            return new AsyncScopedJobDecorator(
                this.container, () => (IJob)jobProducer.GetInstance());
        }
    
        public void ReturnJob(IJob job)
        {
            // This will be handled automatically by Simple Injector
        }
    
        private sealed class AsyncScopedJobDecorator : IJob
        {
            private readonly Container container;
            private readonly Func<IJob> decorateeFactory;
    
            public AsyncScopedJobDecorator(
                Container container, Func<IJob> decorateeFactory)
            {
                this.container = container;
                this.decorateeFactory = decorateeFactory;
            }
    
            public async Task Execute(IJobExecutionContext context)
            {
                using (AsyncScopedLifestyle.BeginScope(this.container))
                {
                    var job = this.decorateeFactory();
                    await job.Execute(context);
                }
            }
        }
    }
    

    Furthermore, you'll need the following registrations:

    var container = new Container();
    
    container.Options.ScopedLifestyle = new AsyncScopedLifestyle();
    
    var factory = new StdSchedulerFactory();
    
    IScheduler scheduler = await factory.GetScheduler();
    
    scheduler.JobFactory = new SimpleInjectorJobFactory(
        container, 
        Assembly.GetExecutingAssembly()); // assemblies that contain jobs
    
    // Optional: register some decorators
    container.RegisterDecorator(typeof(IJob), typeof(LoggingJobDecorator));
    
    container.Verify();