C# job scheduling Quartz.NET learning notes

C# job scheduling Quartz.NET learning notes

 

    1, Brief introduction

    Quartz.NET is a powerful, open source and lightweight job scheduling framework. It is a. NET porting of OpenSymphony's Quartz API. It is rewritten in C# and can be used in WinForm and ASP.NET applications. It is flexible but not complex, and can create simple or complex job scheduling for executing a job. It has many features, such as database support, clustering, plug-ins, support for cron like expressions, and so on.

    Official website: http://www.quartz-scheduler.net/

    Source code: https://github.com/quartznet/quartznet

    Example: http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html

    2, Conceptual interpretation

    Scheduler: job scheduler.

     IJob: job interface, inheriting and implementing Execute,   Write specific job logic for execution.

     JobBuilder: generates a detailed job information (JobDetail) according to the settings.

     Trigger Builder: produces the corresponding trigger according to the rules.

    3, Sample program

    3.1 interface

    Create a WinForm Client, right-click the project - > properties - > Application - > output type, and select console application.

    3.2 reference

    Right click the project - > manage NuGet package - > quartz.net.

    3.2 operation

    Create a new class DataSyncJob and inherit IJob, which represents that it is a job and implements the Execute method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;

namespace LinkTo.Test.Quartz.Client
{
    //start to fight DisallowConcurrentExecution Label let Job Run a one-way run to avoid repetition when you don't finish.
    [DisallowConcurrentExecution]
    public class DataSyncJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            JobDataMap keyValuePairs = context.MergedJobDataMap;
            if (keyValuePairs.Count > 0 && keyValuePairs.Contains("Hello"))
            {
                string value = context.MergedJobDataMap.Get("Hello").ToString();
                return Task.Run(() =>
                {
                    Console.WriteLine(DateTime.Now + $" Hello: {value}" + Environment.NewLine);
                });
            }
            else
            {
                return Task.Run(() =>
                {
                    Console.WriteLine(DateTime.Now + Environment.NewLine);
                });
            }
        }
    }
}

    explain:

    1) Generally speaking, the job needs to be labeled with [disallowcurrentexecution] to avoid being scheduled for execution when the job has not been completed.

    2) The job can receive the parameter (key value) passed by the trigger, and the above job receives the "Hello" parameter.

    3.3 dispatching

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Quartz;
using Quartz.Impl;

namespace LinkTo.Test.Quartz.Client
{
    public partial class Main : Form
    {
        //Scheduler factory
        private ISchedulerFactory factory;
        //Scheduler
        private IScheduler scheduler;

        public Main()
        {
            InitializeComponent();
            //Button status
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }

        /// <summary>
        /// start
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnStart_Click(object sender, EventArgs e)
        {
            //1,Create a scheduler
            factory = new StdSchedulerFactory();
            scheduler = await factory.GetScheduler();
            await scheduler.Start();

            //2,Create a task
            IJobDetail job = JobBuilder.Create<DataSyncJob>().WithIdentity("DataSync", "DataSync_Group").Build();

            //3,Create a trigger(There are four triggers to choose from)
            //4,Add tasks and triggers to the scheduler

            #region Trigger 1: WithSimpleSchedule
            //ITrigger simpleTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_SimpleTrigger", "DataSync_TriggerGroup")
            //    .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
            //    .Build();
            //await scheduler.ScheduleJob(job, simpleTrigger);
            #endregion

            #region Trigger 2: WithDailyTimeIntervalSchedule
            //ITrigger dailyTimeTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_DailyTimeTrigger", "DataSync_TriggerGroup")
            //    .WithDailyTimeIntervalSchedule(x => x.OnEveryDay().WithIntervalInSeconds(5))
            //    .Build();
            //await scheduler.ScheduleJob(job, dailyTimeTrigger);
            #endregion

            #region Trigger 3: WithCalendarIntervalSchedule
            //ITrigger calendarTrigger = TriggerBuilder.Create()
            //    .WithIdentity("DataSync_CalendarTrigger", "DataSync_TriggerGroup")
            //    .WithCalendarIntervalSchedule(x => x.WithIntervalInSeconds(5))
            //    .Build();
            //await scheduler.ScheduleJob(job, calendarTrigger);
            #endregion

            #region Trigger 4: WithCronSchedule(With transfer parameters"Hello")
            ITrigger cronTrigger = TriggerBuilder.Create()
                .WithIdentity("DataSync_CronTrigger", "DataSync_TriggerGroup")
                .WithCronSchedule("0/5 * * * * ?")
                .UsingJobData("Hello", Guid.NewGuid().ToString())
                .Build();
            await scheduler.ScheduleJob(job, cronTrigger);
            #endregion

            //5,Start execution
            await scheduler.Start();

            //Button status
            btnStart.Enabled = false;
            btnStop.Enabled = true;
        }

        /// <summary>
        /// stop it
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {
            if (scheduler != null)
            {
                scheduler.Shutdown(true);
            }
            //Button status
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }
    }
}

    explain:

    1) Both Job and Trigger use * * Group in WithIdentity. Here, Group is used for classification, which is equivalent to a namespace.

    2) There are four types of triggers, namely WithSimpleSchedule, WithDailyTimeIntervalSchedule, WithCalendarIntervalSchedule and WithCronSchedule. The first and fourth types are commonly used.

    3) The WithCronSchedule trigger uses a Cron expression.

    3.4 results

    4, Cron expression

    1) Online Cron Expression Builder: https://cron.qqe2.com/

    2) Official English Introduction: https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/crontrigger.html

    3) The Cron expression is easy to understand as a whole. There is only one thing to pay attention to the usage of the "?" sign. "?" can be used in Day of Month and Day of Week, such as the 31st minute of each hour on the 1st of each month. The correct expression is: * 31 * 1 *?, It can't be: * 31 * 1 * *, because it represents any day of the week.

Cron The expression consists of 7 segments: second minute hour day month week year (optional)

"-": Indicates the range, MON-WED It means Monday to Wednesday.
",": Indicates enumeration, MON,WEB Means Monday and Wednesday.
"*": express"each",Daily, monthly, weekly, annual, etc.
"/": Represents increment, 0/15(Minute) means every 15 minutes, starting after 0 minute; three/20 Means every 20 minutes, starting after 3 minutes.
"?": It can only appear in day and week segments, indicating that no specific value is specified.
"L": It can only appear in days and weeks, right Last An abbreviation for, for example, the last day of a month or the last day of a week (Saturday).
"W": Represents a working day, the nearest working day to a given value.
"#": Indicates the day of the week in a month,"6#3 "means the third Friday of each month (1=SUN...6=FRI,7=SAT).

    4) Official example:

expressionexplain
0 0 12 * * ? Triggered at 12 noon every day
0 15 10 ? * * Triggered every morning at 10:15
0 15 10 * * ? Triggered every morning at 10:15
0 15 10 * * ? * Triggered every morning at 10:15
0 15 10 * * ? 2005 Triggered at 10:15 a.m. every day in 2005
0 * 14 * * ? Triggered every 1 minute between 2 p.m. and 2:59 p.m. every day
0 0/5 14 * * ? Triggered every 5 minutes between 2 p.m. and 2:55 p.m. every day
0 0/5 14,18 * * ? Triggered every 5 minutes between 2 p.m. and 2:55 p.m. and between 6 p.m. and 6:55 p.m
0 0-5 14 * * ? Triggered every 1 minute between 2 p.m. and 2:05 p.m. every day
0 10,44 14 ? 3 WED Triggered at 2:10 p.m. and 2:44 p.m. on Wednesday in March every year
0 15 10 ? * MON-FRI Triggered from Monday to Friday at 10:15 a.m
0 15 10 15 * ? Triggered at 10:15 a.m. on the 15th of each month
0 15 10 L * ? Triggered at 10:15 a.m. on the last day of each month
0 15 10 L-2 * ? Triggered at 10:15 a.m. from the second day to the last day of each month
0 15 10 ? * 6L Triggered at 10:15 a.m. on the last Friday of each month
0 15 10 ? * 6L Triggered at 10:15 a.m. on the last Friday of each month
0 15 10 ? * 6L 2002-2005 Triggered at 10:15 a.m. on the last Friday of each month from 2002 to 2005
0 15 10 ? * 6#3 Triggered at 10:15 a.m. on the third Friday of each month
0 0 12 1/5 * ? Triggered every 5 days every month at 12 p.m. (noon), starting from the first day of each month
0 11 11 11 11 ? Triggered at 11:11 a.m. on November 11

    5, One line of code for scheduling

    Create a new TestJob:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;

namespace LinkTo.Test.Quartz.Client
{
    //start to fight DisallowConcurrentExecution Label let Job Run a one-way run to avoid repetition when you don't finish.
    [DisallowConcurrentExecution]
    public class TestJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine(DateTime.Now + Environment.NewLine);
            });
        }
    }
}

    Create a new scheduling encapsulation class QuartzFactory:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Triggers;

namespace LinkTo.Test.Quartz.Client
{
    public class QuartzFactory
    {
        //Scheduler factory
        private static ISchedulerFactory factory = null;
        //Scheduler
        private static IScheduler scheduler = null;

        /// <summary>
        /// Constructor
        /// </summary>
        static QuartzFactory()
        {
            factory = new StdSchedulerFactory();
            scheduler = factory.GetScheduler().Result;
            scheduler.Start();
        }

        #region Trigger 1: add Job
        /// <summary>
        /// Trigger 1: add Job And run in the form of cycle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime"></param>
        /// <param name="jobDataMap"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime, Dictionary<string, object> jobDataMap) where T : IJob
        {
            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").Build();
            jobCheck.JobDataMap.PutAll(jobDataMap);
            ISimpleTrigger triggerCheck = new SimpleTriggerImpl(jobName + "_SimpleTrigger", 
                jobName + "_TriggerGroup",
                startTime,
                null,
                SimpleTriggerImpl.RepeatIndefinitely,
                simpleTime);
            return scheduler.ScheduleJob(jobCheck, triggerCheck).Result;
        }

        /// <summary>
        /// Trigger 1: add Job And run in the form of cycle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime">Msec </param>
        /// <param name="mapKey"></param>
        /// <param name="mapValue"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime, string mapKey, object mapValue) where T : IJob
        {
            Dictionary<string, object> jobDataMap = new Dictionary<string, object>
            {
                { mapKey, mapValue }
            };
            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime), jobDataMap);
        }

        /// <summary>
        /// Trigger 1: add Job And run in the form of cycle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, TimeSpan simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, startTime, simpleTime, new Dictionary<string, object>());
        }

        /// <summary>
        /// Trigger 1: add Job And run in the form of cycle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="startTime"></param>
        /// <param name="simpleTime">Msec </param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, DateTimeOffset startTime, int simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, startTime, TimeSpan.FromMilliseconds(simpleTime));
        }

        /// <summary>
        /// Trigger 1: add Job And run in the form of cycle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="simpleTime">Msec </param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, int simpleTime) where T : IJob
        {
            return AddJob<T>(jobName, DateTime.UtcNow.AddMilliseconds(1), TimeSpan.FromMilliseconds(simpleTime));
        }
        #endregion

        #region Trigger 4: add Job
        /// <summary>
        /// Trigger 4: add Job And run in the form of fixed points
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="cronTime"></param>
        /// <param name="jobDataMap"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, string cronTime, string jobData) where T : IJob
        {
            IJobDetail jobCheck = JobBuilder.Create<T>().WithIdentity(jobName, jobName + "_Group").UsingJobData("jobData", jobData).Build();
            ICronTrigger cronTrigger = new CronTriggerImpl(jobName + "_CronTrigger", jobName + "_TriggerGroup", cronTime);
            return scheduler.ScheduleJob(jobCheck, cronTrigger).Result;
        }

        /// <summary>
        /// Trigger 4: add Job And run in the form of fixed points
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jobName"></param>
        /// <param name="cronTime"></param>
        /// <returns></returns>
        public static DateTimeOffset AddJob<T>(string jobName, string cronTime) where T : IJob
        {
            return AddJob<T>(jobName, cronTime, null);
        }
        #endregion

        /// <summary>
        /// Modify trigger time overload
        /// </summary>
        /// <param name="jobName">Job name</param>
        /// <param name="timeSpan">TimeSpan</param>
        /// </summary>
        public static void UpdateTime(string jobName, TimeSpan simpleTimeSpan)
        {
            TriggerKey triggerKey = new TriggerKey(jobName + "_SimpleTrigger", jobName + "_TriggerGroup");
            SimpleTriggerImpl simpleTriggerImpl = scheduler.GetTrigger(triggerKey).Result as SimpleTriggerImpl;
            simpleTriggerImpl.RepeatInterval = simpleTimeSpan;
            scheduler.RescheduleJob(triggerKey, simpleTriggerImpl);
        }

        /// <summary>
        /// Modify trigger time overload
        /// </summary>
        /// <param name="jobName">Job name</param>
        /// <param name="simpleTime">Minutes</param>
        /// <summary>
        public static void UpdateTime(string jobName, int simpleTime)
        {
            UpdateTime(jobName, TimeSpan.FromMinutes(simpleTime));
        }

        /// <summary>
        /// Modify trigger time overload
        /// </summary>
        /// <param name="jobName">Job name</param>
        /// <param name="cronTime">Cron expression</param>
        public static void UpdateTime(string jobName, string cronTime)
        {
            TriggerKey triggerKey = new TriggerKey(jobName + "_CronTrigger", jobName + "_TriggerGroup");
            CronTriggerImpl cronTriggerImpl = scheduler.GetTrigger(triggerKey).Result as CronTriggerImpl;
            cronTriggerImpl.CronExpression = new CronExpression(cronTime);
            scheduler.RescheduleJob(triggerKey, cronTriggerImpl);
        }

        /// <summary>
        /// Pause all Job
        /// </summary>
        public static void PauseAll()
        {
            scheduler.PauseAll();
        }

        /// <summary>
        /// Restore all Job
        /// </summary>
        public static void ResumeAll()
        {
            scheduler.ResumeAll();
        }

        /// <summary>
        /// Delete assignment Job
        /// </summary>
        /// <param name="jobName"></param>
        public static void DeleteJob(string jobName)
        {
            JobKey jobKey = new JobKey(jobName, jobName + "_Group");
            scheduler.DeleteJob(jobKey);
        }

        /// <summary>
        /// Unload timer
        /// </summary>
        /// <param name="isWaitForToComplete">Wait Job Execution complete</param>
        public static void Shutdown(bool isWaitForToComplete)
        {
            scheduler.Shutdown(isWaitForToComplete);
        }
    }
}

     One line of code implements the job scheduling button code:

        /// <summary>
        /// One line of code for job scheduling
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOneCode_Click(object sender, EventArgs e)
        {
            if (btnOneCode.Text == "One line of code for job scheduling")
            {
                string cronTime = "0/5 * * * * ?";
                QuartzFactory.AddJob<TestJob>("TestJob", cronTime);
                btnOneCode.Text = "Stop";
            }
            else
            {
                QuartzFactory.DeleteJob("TestJob");
                btnOneCode.Text = "One line of code for job scheduling";
            }
        }

 

    Reference from:

    https://www.cnblogs.com/best/p/7658573.html

 

Keywords: C# nuget

Added by Niel Roos on Fri, 03 Dec 2021 10:47:00 +0200