Scheduling using Quartz.NET with Autofac DI

12 Aug 2017

We often have task like sending mail to bulk list of emails or periodically watching for data update on a target endpoint/web services. These kind of tasks normally wont require user interaction and are expected to be automatically triggered on particular duration & run as a background process.

The best way to run such repeated tasks or jobs are by building a console application and scheduling it to run as a windows services. But under some scenarios., like you don't have access to a server machine to schedule your service or on a shared hosting space i.e., if you are forced to run these jobs from the web application. 

ASP.Net net has System.Timers.Timer that can help you on basic level to schedule job.

public static System.Timers.Timer timer = new System.Timers.Timer();

void Application_Start(object sender, EventArgs e)
{
   Application.Add("Counter", 0);
   timer.Interval = 5000;
   timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
   timer.Start();
}

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
   int x = (int)Application["Counter"];
   x++;
   Application["Counter"] = x;
}

But problem with this is that it is not flexible, when you want it run at particular order. i.e., once a week, on just on particular days in a week etc., So Quartz.Net helps you to setup an enterprise level robust and flexible schedulers.

In this article I will cover on how to set a robust scheduler using Quartz and Autofac Dependency Injection library. 

Step 1: 
Install Autofac.Extras.Quartz nuget package, this provides a nested lifetime scope for all IJobs and automatically disposes the scope when the job is completed.

Step 2:
Quartz Registration, Configuration & initiation(start): 

public partial class Startup
{
   public static void ConfigureContainer(IAppBuilder app)
   {
      ContainerBuilder builder = new ContainerBuilder();
      RegisterScheduler(builder);
      IContainer container = builder.Build();
      ConfigureScheduler(container);
   }
   private static void RegisterScheduler(ContainerBuilder builder)
   {
     // configure and register Quartz
     var schedulerConfig = new NameValueCollection {
      {"quartz.threadPool.threadCount", "3"},
      // {"quartz.threadPool.threadNamePrefix", "MyScheduleWorker"} comment this in Quartz version 3+,
      {"quartz.scheduler.threadName", "MyScheduler"}
     };

     builder.RegisterModule(new QuartzAutofacFactoryModule
     {
      ConfigurationProvider = c => schedulerConfig
     });

     builder.RegisterModule(new QuartzAutofacJobsModule(typeof(ScrapperJob).Assembly));
     builder.RegisterType<JobScheduler>().AsSelf();
   }

   private static void ConfigureScheduler(IContainer container)
   {
     var scheduler = container.Resolve<JobScheduler>();
     scheduler.Start();
   }
}

Step 3:
Sample Scheduler configuration & Job trigger conditions

public class JobScheduler
    {
        private readonly IScheduler _scheduler;

        public JobScheduler(IScheduler scheduler)
        {
            _scheduler = scheduler;
        }

        public void Start()
        {
            _scheduler.Start();

            IJobDetail job = JobBuilder.Create<ScrapperJob>()
                .WithIdentity("ScrapGoldRate", "admin")
                .Build();

            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("ScrapGoldRateTrigger", "admin")
                .WithSimpleSchedule(x => x
                    .RepeatForever()
                    .WithIntervalInMinutes(5)
                 )
                .StartNow()
                .Build();

            _scheduler.ScheduleJob(job, trigger);
        }
    }

Step 4:
Sample Job code

public class ScrapperJob : IJob
{
    private IRepositoryService _repo;
    public ScrapperJob(IRepositoryService repo)
    {
        _repo = repo;
    }
    /* Quartz 2.x
    public void Execute(IJobExecutionContext context)
    {
        // Your logics
    } */
    
    // Quartz 3+ - You can use async jobs and also return some useful info
    // back to the scheduler on each run
    async Task IJob.Execute(IJobExecutionContext context)
    {
        await DoSomeAsyncWork();
    }

}

With all these you should be able to properly inject the types or services in to your Quartz.Net job.

Here is a sample application that runs and fetches Gold information using Quartz scheduler -> https://davidsekar.com/gold-silver-platinum-rates-in-chennai

Related Posts