Search code examples
c#.net-coreinversion-of-controlautofacquartz.net

Quartz.NET scheduling issues in Autofac container


I have an Autofac container that I wish to add a lifetime Quartz.NET scheduler to using Autofac.Extras.Quartz. With the code as is below, a Quartz scheduler is instantiated with the set properties, but it seems the scheduler is killed off after the initialization step.

I first call Initialize from my Program.cs to create a new container for MyModule.

MyModuleStartup.cs

private static IContainer? _container;
public static void Initialize()
{
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule(new QuartzModule());
    _container = containerBuilder.Build();
    MyCompositionRoot.SetContainer(_container);
    QuartzStartup.Initialize(_container);
}

I register and configure QuartzAutofacFactoryModule and QuartzAutofacJobsModule as described in the package's documentation.

QuartzModule.cs

public class QuartzModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        var schedulerConfiguration = new NameValueCollection { { "quartz.scheduler.instanceName", "MyScheduler" } };
        builder.RegisterModule(new QuartzAutofacFactoryModule
        {
            ConfigurationProvider = _ => schedulerConfiguration
        });
        builder.RegisterModule(new QuartzAutofacJobsModule(typeof(MyJob).Assembly));
    }
}

I then initialize Quartz.NET.

QuartzModule.cs

internal static class QuartzStartup
{
    private static IScheduler? _scheduler;

    internal static void Initialize(IContainer container)
    {
        _scheduler = container.Resolve<IScheduler>();
        _scheduler.Start().GetAwaiter().GetResult();
    
        var myJob = JobBuilder.Create<MyJob>().Build();

        var myTrigger = TriggerBuilder.Create().StartNow()
            .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()).Build();

        // I can confirm here that the instance name property of scheduler is MyScheduler
        _scheduler.ScheduleJob(myJob, myTrigger).GetAwaiter().GetResult();

    }
}

MyJob is a simple IJob class with a single debug statement in its overridden Execute method (with a debug on the line that is never hit).

MyJob.cs

public abstract class MyJob: IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        // Based on debugging, this line is never hit.
        Debug.WriteLine($"Job running at {DateTime.Now.ToString()}");
    }
}

How can I properly instantiate a lifetime instance of Quartz.NET withing my container?


Solution

  • The QuartzAutofacJobsModule doesn't register the class MyJob because it is abstract since abstract classes can't be instantiated. Remove that and everything works as expected.

    public class MyJob: IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            // Based on debugging, this line is never hit.
            Debug.WriteLine($"Job running at {DateTime.Now.ToString()}");
    
            await Task.CompletedTask;
        }
    }
    

    Make sure your program doesn't exit before the first trigger fires. I've added a temporary delay at the end and made proper use of async/await just so the sample code you provided doesn't exit immediately.

    internal static async Task Initialize(IContainer container)
    {
        _scheduler = container.Resolve<IScheduler>();
        await _scheduler.Start();
        
        var myJob = JobBuilder.Create<MyJob>().Build();
    
        var myTrigger = TriggerBuilder.Create().StartNow()
            .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()).Build();
    
        // I can confirm here that the instance name property of scheduler is MyScheduler
        await _scheduler.ScheduleJob(myJob, myTrigger);
        await Task.Delay(10000);
    }