? ? 之前項目采用JAVA 的 Quartz 進行定時服調度務處理程序,目前在.NET下面使用依然可以完成相同的工作任務,其實什么語言不重要,關鍵是我們要學會利用語言實現價值。它是一個簡單的執行任務計劃的組件,基本包括這三部分:Job(作業)、Trigger(觸發器)、scheduler(調度器)。
? ? ?? 1.Job 作業:需要在任務計劃中執行的具體邏輯操作
??????? 2.Trigger 觸發器:需要什么時間什么規則來去執行Job 作業
??????? 3.scheduler 調度器 :將Job 和 Trigger 注冊到 scheduler 調度器中,主要負責協調Job、Trigger 的運行
.NET可以做成服務端方式也可以做成web端處理,本方法是采用web的方式處理,話不多說直接上干活。在這里借鑒了別人的方式但是別人的有些很多漏洞和錯誤,我也進行了拋磚引玉加以完善。
首先先創建一個新項目,新建一個類庫JobLibrary項目庫:
一個Job 創建一個實例類,創建兩個實例類一個是UpdateCompleteJob.cs、UpdateAutoCancelStateJob.cs (之所以創建兩個Job是為了能方便大家了解這個組件可以同時執行多個任務)
一:UpdateCompleteJob.cs 代碼如下:
namespace JobLibrary {// quartz.net 禁止并發執行DisallowConcurrentExecution是禁止相同JobDetail同時執行,而不是禁止多個不同JobDetail同時執行。建議加上該特性,防止由于任務執行時間太長,長時間占用資源,導致其它任務堵塞。 [DisallowConcurrentExecution]public class UpdateCompleteJob : IJob{TRA_BargainOrder_Test ExpressModel;/// <summary>///在Job 中我們必須實現IJob接口中的Execute 這個方法,才能正確的使用Job/// </summary>public async Task Execute(IJobExecutionContext context){using (var _DbContext = new DefaultDbContext()){//var tarorder = new TRA_BargainOrder_Test()//{// BargainOrderCode="12345688899",// OrderStatus=1//};//_DbContext.TRA_BargainOrders.Add(tarorder);////保存記錄,返回受影響的行數//int record = _DbContext.SaveChanges();//Console.WriteLine("添加{0}記錄成功", record);var query = _DbContext.TRA_BargainOrders.Where(c => c.OrderStatus == (int)EnumHelper.OrderStatus.Sended);//var query = _DbContext.TRA_BargainOrders.Where(c => c.OrderStatus == (int)EnumHelper.OrderStatus.Sended// && c.PayStatus == (int)EnumHelper.PayStatus.Paid).OrderBy(c => c.CreateTime).ToList().Take(20);foreach (var item in query){if (!string.IsNullOrEmpty(item.ExpressCode)){//根據快遞單號獲取快遞訂單信息try{ExpressModel = await _DbContext.TRA_BargainOrders.SingleOrDefaultAsync(s => s.ExpressCode == item.ExpressCode);}catch (Exception e){new Exception(e.Message);}//確定 已簽收 修改訂單狀態 已完成if (ExpressModel.OrderStatus ==1&& ExpressModel.ischeck == 1){var order = _DbContext.TRA_BargainOrders.FirstOrDefault(c => c.BargainOrderCode == item.BargainOrderCode);// var order = _DbContext.Set<TRA_BargainOrder_Test>().FirstOrDefault(c => c.BargainOrderCode == item.BargainOrderCode);order.OrderStatus = (int)EnumHelper.OrderStatus.Over;order.FlowStatus = (int)EnumHelper.FlowStatus.Over;order.UpdateTime = DateTime.Now;_DbContext.TRA_BargainOrders.Attach(order);_DbContext.Entry(order).State = EntityState.Modified;_DbContext.TRA_BargainOrders.AddOrUpdate(order);}}}//保存數據庫不能放到循環中操作 try{_DbContext.SaveChanges();}catch (Exception E){ throw new Exception(E.Message); }}}} }
二:UpdateAutoCancelStateJob.cs 代碼如下:
namespace JobLibrary {//在Quartz.Net中,一個job(作業)即為一個類,為了讓job能在Quartz.Net的體系中執行,我們必須實現Quartz.Net提供的IJob接口的Execute方法,如本例所實現的IJob接口UpdateAutoCancelStateJob類: [DisallowConcurrentExecution]public class UpdateAutoCancelStateJob : IJob{public async Task Execute(IJobExecutionContext context){using (var _DbContext = new DefaultDbContext()){var order = await _DbContext.TRA_BargainOrders.FirstOrDefaultAsync(c => c.OrderStatus == (int)EnumHelper.OrderStatus.UnSend && c.PayStatus == (int)EnumHelper.PayStatus.UnPaid);if (order!=null){if (DateDiff(DateTime.Now, order.CreateTime) > 30){order.OrderStatus = (int)EnumHelper.OrderStatus.Cancel;order.FlowStatus = (int)EnumHelper.FlowStatus.Cancel;order.UpdateTime = DateTime.Now;_DbContext.SaveChanges();}}}}//計算時間差的方法private int DateDiff(DateTime DateTime1, DateTime DateTime2){TimeSpan tss = Convert.ToDateTime(DateTime1) - Convert.ToDateTime(DateTime2);int dateDiff = Convert.ToInt32(tss.TotalMinutes);return dateDiff;}} }
以下是web啟動項目下的
三:設置Trigger 觸發器,在實際中我是將Trigger和Job 直接注冊到 scheduler 調度器中;就是需要將類庫生成的DLL 拷貝到你的需要執行的項目的文件中
觸發器的JobManage代碼如下:
public class JobManage{private static ISchedulerFactory sf = new StdSchedulerFactory();//調度器private static IScheduler scheduler;/// <summary>/// 讀取調度器配置文件的開始時間/// </summary>//public static void StartScheduleFromConfig()public static async void StartScheduleFromConfigAsync(){string currentDir = AppDomain.CurrentDomain.BaseDirectory;try{XDocument xDoc = XDocument.Load(Path.Combine(currentDir, "JobScheduler.config"));var jobScheduler = from x in xDoc.Descendants("JobScheduler") select x;var jobs = jobScheduler.Elements("Job");XElement jobDetailXElement, triggerXElement;//獲取調度器scheduler = await sf.GetScheduler();//聲明觸發器 CronTriggerImpl cronTrigger;foreach (var job in jobs){//加載程序集joblibaray Assembly ass = Assembly.LoadFrom(Path.Combine(currentDir, job.Element("DllName").Value));//獲取任務名字jobDetailXElement = job.Element("JobDetail");//獲取任務觸發的時間triggerXElement = job.Element("Trigger");JobDetailImpl jobDetail = new JobDetailImpl(jobDetailXElement.Attribute("job").Value,jobDetailXElement.Attribute("group").Value,ass.GetType(jobDetailXElement.Attribute("jobtype").Value));if (triggerXElement.Attribute("type").Value.Equals("CronTrigger")){cronTrigger = new CronTriggerImpl(triggerXElement.Attribute("name").Value,triggerXElement.Attribute("group").Value,triggerXElement.Attribute("expression").Value);//添加定時器await scheduler.ScheduleJob(jobDetail, cronTrigger);}}//開始執行定時器await scheduler.Start();}catch (Exception E){throw new Exception(E.Message);}}/// <summary>/// 關閉定時器/// </summary>public static void ShutDown(){if (scheduler != null && !scheduler.IsShutdown){scheduler.Shutdown();}}/// <summary>/// 從Scheduler?移除當前的Job,修改Trigger???/// </summary>/// <param name="jobExecution"></param>/// <param name="time"></param>public static void ModifyJobTime(IJobExecutionContext jobExecution, String time){scheduler = jobExecution.Scheduler;ITrigger trigger = jobExecution.Trigger;IJobDetail jobDetail = jobExecution.JobDetail;if (trigger != null){CronTriggerImpl ct = (CronTriggerImpl)trigger;//?移除當前進程的Job????? scheduler.DeleteJob(jobDetail.Key);//?修改Trigger?????ct.CronExpressionString = time;Console.WriteLine("CronTrigger?getName?" + ct.JobName);//?重新調度jobDetail????? scheduler.ScheduleJob(jobDetail, ct);}}}
四:配置文件,主要是控制任務執行的時間和Job 的加載 JobScheduler.config
<?xml version="1.0" encoding="utf-8"?> <configuration><!--配置文件,主要是控制任務執行的時間和Job 的加載 配置中重要的幾個屬性 <DllName>JobLibrary.dll</DllName> dll的名字 ;jobtype 屬性是dll名字+實例類的名字;expression 這個是設置執行的時間--><JobScheduler><Job Description="作業1"><DllName>JobLibrary.dll</DllName><JobDetail job="test1" group="test1Group" jobtype="JobLibrary.UpdateCompleteJob" /><Trigger name="test1" group="test1Group" type="CronTrigger" expression="0 0/50 * * * ?" /> <!--0 0/10 * * * ? 10分鐘--> </Job><Job Description="作業2"><DllName>JobLibrary.dll</DllName><JobDetail job="test2" group="test2Group" jobtype="JobLibrary.UpdateAutoCancelStateJob" /><Trigger name="test2" group="test2Group" type="CronTrigger" expression="0/10 * * * * ?" /> <!--0/10 * * * * ? 10秒--> <!-- 每天凌晨1點執行一次:0 0 1 * * ? --> <!--每天凌晨1點30分執行一次:0 30 1 * * ?--> <!--每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ? --><!-- "0 0/5 14,18 * * ?" ? ?每天14點或18點中,每5分鐘觸發--></Job></JobScheduler><system.web><compilation debug="true" targetFramework="4.6.1" /><httpRuntime targetFramework="4.6.1" /></system.web></configuration>
五:需要將scheduler 調度器注冊到程序中;在程序中Global.asax.cs 中文件中添加注冊,在這里啟動執行任務
//需要將scheduler 調度器注冊到程序中;在程序中Global.asax.cs 中文件中添加注冊,在這里啟動執行任務。protected void Application_Start(){AreaRegistration.RegisterAllAreas();FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);//執行的任務 JobManage.StartScheduleFromConfigAsync();}//當網站關閉時結束正在執行的工作protected void Application_End(object sender, EventArgs e){// 在應用程序關閉時運行的代碼 JobManage.ShutDown();}
?六:至此可以啟動服務完成定時調度處理任務了
?