Constructor injection with Quartz.NET and Simple Injector
Asked Answered
S

3

40

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
    }
Safire answered 28/1, 2013 at 12:35 Comment(0)
S
48

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();
Slaver answered 28/1, 2013 at 13:2 Comment(10)
Thanks for this. A slight question, the class of StdSchedulerFactory has 2 constructors, is there anyway of telling simpleinjector to use the zero param constructor?Safire
@Thewads: I updated my answer. Since StdSchedulerFactory is a singleton and a Stable Dependency (part of a framework) it is safe (perhaps even advisable) to manually create it.Slaver
I had to search for it and thought it would be helpful to note that the following quartz configuration is how you tell it to use the injection friendly job factory implementation:<add key="quartz.scheduler.jobFactory.type" value="YourNamespace.SimpleInjectorJobFactory, YourAssembly"/>Reserve
I had to register the scheduler slightly differently to above to put the jobFactory in. Using XML did not work since it forces a default constructor requirement. So in addition to Steven's post I added container.RegisterSingle<IScheduler>(() => {var scheduler = schedulerFactory.GetScheduler(); scheduler.JobFactory = container.GetInstance<IJobFactory>(); return scheduler; });Platelayer
What is isDecorator? Also, the type of jobProducers should be Dictionary<Type, InstanceProducer>.Reverberation
Great! Also note that with Quartz.Net 2.0, one needs to implement public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) {...}. And not sure if there should be any cleanup call implemented in public void ReturnJob(IJob job){...}.Reverberation
This doesn't seem to work with the latest version of Quartz and SimpleInjector. I do not have an ILoadServiceScheduler, TimerScheduler, LoggingJobDecorator. Also how do I get the applicationAssemblies? I'm quite confused here and can't figure this one out. :)Zebadiah
For anyone having issues with this, have a look at a working example here: github.com/tynor88/Topshelf.SimpleInjectorSettlings
I was having some issue, and suddenly found my old comment again. Now, for myself and others: here is the full example I was referencing before: github.com/tynor88/Topshelf.SimpleInjector/blob/dev/Samples/…Settlings
Quartz V3 implementation #49058360Cratch
P
3

Late to the party, but https://github.com/hbiarge/Quartz.Unity works well for combining Quartz.NET and Unity.

IUnityContainer container = new UnityContainer();
container.AddNewExtension<Quartz.Unity.QuartzUnityExtension>();
// do your other Unity registrations
IScheduler scheduler = container.Resolve<IScheduler>();

scheduler.ScheduleJob(
    new JobDetailImpl(myCommandName, typeof(MyCommand)),
    TriggerBuilder.Create()
        .WithCronSchedule(myCronSchedule)
        .StartAt(startTime)
        .Build()
);
scheduler.Start();
Powe answered 14/6, 2016 at 21:7 Comment(0)
J
1

There are few steps to use Quartz.net with dependency injection engine from asp.net core.

Add nuget package to your project:

Microsoft.Extensions.DependencyInjection

Create custom JobFactory:

public class JobFactory : IJobFactory
{
    protected readonly IServiceProvider _serviceProvider;

     public JobFactory(IServiceProvider serviceProvider) 
         => _serviceProvider = serviceProvider;

     public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
         => _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob;

     public void ReturnJob(IJob job) 
         => (job as IDisposable)?.Dispose();
 }

Specify JobFactory when configuring scheduler:

 var scheduler = await StdSchedulerFactory.GetDefaultScheduler();
 scheduler.JobFactory = new JobFactory(_serviceProvider);

For someone can be usefull example of win service with Quartz.net and DI (from asp.net core) on the board:

public class WinService : ServiceBase
{
    private Scheduler _scheduleManager;

    private readonly Startup _startup;

    public WinService()
    {
        ServiceName = "SomeWinService";
        _startup = new Startup();
    }

    static void Main(string[] args)
    {
        var service = new WinService();

        // Working as Windows-service
        if (Console.IsInputRedirected && Console.IsOutputRedirected)
        {
            ServiceBase.Run(service);
        }
        // Working as console app
        else
        {
            service.OnStart(args);
            Console.WriteLine("Press any key to stop...");
            Console.ReadKey();
            service.OnStop();
        }
    }
 
    protected override void OnStart(string[] args)
    {
        _startup.RegisterServices();
        _scheduleManager = new Scheduler(_startup.ServiceProvider);
        _scheduleManager.StartTracking().Wait();
    }

    protected override void OnPause()
        => _scheduleManager.PauseTracking().Wait();

    protected override void OnContinue()
        => _scheduleManager.ResumeTracking().Wait();
 
    protected override void OnStop()
    {
        _scheduleManager.StopTracking().Wait();
        _startup.DisposeServices();
    }
}

public class Startup
{
    private IServiceProvider _serviceProvider;

    public IServiceProvider ServiceProvider => _serviceProvider;

    public void RegisterServices()
    {        
        _serviceProvider = new ServiceCollection()
            //.AddTransient(...)
            //.AddScoped(...)
            //.AddSingleton(...)
            .BuildServiceProvider();

    }

    public void DisposeServices()
    {
        if (_serviceProvider == null)
            return;

        if (_serviceProvider is IDisposable)
        {
            ((IDisposable)_serviceProvider).Dispose();
        }
    }
}

public class Scheduler
{        
    private readonly IServiceProvider _serviceProvider;
   
    private IScheduler _scheduler;
   
    public Scheduler(IServiceProvider serviceProvider) 
        => _serviceProvider = serviceProvider;
   
    public async Task StartTracking()
    {
        _scheduler = await StdSchedulerFactory.GetDefaultScheduler();
        _scheduler.JobFactory = new JobFactory(_serviceProvider);
        await _scheduler.Start();
       
        // Schedule your jobs here
    }
  
    public async Task PauseTracking() => await _scheduler?.PauseAll();
   
    public async Task ResumeTracking() => await _scheduler?.ResumeAll();
  
    public async Task StopTracking() => await _scheduler?.Shutdown();
}
Jollanta answered 27/11, 2020 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.