Thursday, December 29, 2016

Unity and Quartz .NET

In my current project we need to use Quartz .NET for scheduled tasks. Since we already use Unity IoC it makes sense to inject all dependencies into quartz jobs. After googling I found this repository but example in that soulution as well as the nuget package keep throwing exceptions. Turned out to be that in order to integrate any IoC container with quartz it's enough to do three simple steps.

1. Implement IJobFactory interface

public class UnityJobFactory : IJobFactory
{
    private readonly IUnityContainer _container;

    public UnityJobFactory(IUnityContainer container)
    {
        _container = container;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        return _container.Resolve(bundle.JobDetail.JobType) as IJob;
    }

    public void ReturnJob(IJob job)
    {
    }
}

2. Inherit StdSchedulerFactory

public class UnitySchedulerFactory : StdSchedulerFactory
{
    private readonly IJobFactory _jobFactory;

    public UnitySchedulerFactory(IJobFactory jobFactory)
    {
        _jobFactory = jobFactory;
    }

    protected override IScheduler Instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)
    {
        qs.JobFactory = _jobFactory;

        return base.Instantiate(rsrcs, qs);
    }
}

3. Register those two implementations in the container

container.RegisterType<IJobFactory, UnityJobFactory>();

container.RegisterType<ISchedulerFactory, UnitySchedulerFactory>();

In adition to keep everything more separated, independat and easier to use, we can extract registration into a Unity extension.

public class QuartzUnityExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        this.Container.RegisterType<IJobFactory, UnityJobFactory>();

        this.Container.RegisterType<ISchedulerFactory, UnitySchedulerFactory>();
    }
}

And of cause an example.


public class MyJob : IJob
{
    private readonly ISomeService _someService;

    public MyJob(ISomeService someService)
    {
        _someService = someService;
    }

    public void Execute(IJobExecutionContext context)
    {
        Console.WriteLine(_someService.GiveMeSomething());
    }
}

public interface ISomeService
{
    string GiveMeSomething();
}

public class SomeService : ISomeService
{
    public string GiveMeSomething()
    {
        return "Something";
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var container = new UnityContainer();

        container.AddNewExtension<QuartzUnityExtension>();

        container.RegisterType<ISomeService, SomeService>();

        var scheduler = container.Resolve<ISchedulerFactory>().GetScheduler();

        scheduler.ScheduleJob(
            new JobDetailImpl("myJob", typeof(MyJob)),
            new CalendarIntervalTriggerImpl("TestTrigger", IntervalUnit.Second, 2)
        );

        scheduler.Start();

        Thread.Sleep(TimeSpan.FromSeconds(10));

        scheduler.Shutdown();
    }
}