今天這篇文章我將經過實例代碼帶着你們一步一步經過abp vNext這個asp.net core的快速開發框架來進行Quartz.net定時任務調度的管理界面的開發。大夥最好跟着一塊兒敲一下代碼,固然源碼我會上傳到github上,有興趣的小夥伴能夠在文章底部查看源碼連接。html
做者:依樂祝
原文連接:http://www.javashuo.com/article/p-xsaecxlv-kt.htmlgit
有幾天沒更新博客了,一方面由於比較忙,另外一方面是由於最近在準備組織咱們霸都合肥的.NET技術社區首次非正式的線下聚會,忙着聯繫人啊,這裏歡迎有興趣的小夥伴加我wx:jkingzhu進行詳細的瞭解,固然也歡迎同行加我微信,而後我拉你進入咱們合肥.NET技術社區微信羣跟大夥進行交流。github
開始以前還有必要跟大夥說一下abp vNext以及Quartz.net是什麼,防止有小白。若是對這兩個概念很是熟悉的話能夠直接閱讀下一節。項目最終實現的效果以下圖所示:web
提及abp vNext就要從另外一個概念開始提及了,那就是大名鼎鼎的ABP了。
ABP 官方的介紹是:ASP.NET Boilerplate 是一個用最佳實踐和流行技術開發現代 WEB 應用程序的新起點,它旨在成爲一個通用的 WEB 應用程序基礎框架和項目模板。基於 DDD 的經典分層架構思想,實現了衆多 DDD 的概念(但沒有實現全部 DDD 的概念)。
而ABPVNext的出現是爲了拋棄掉.net framework 版本下的包袱,從新啓動的 abp 框架,目的是爲了放棄對傳統技術的支持,讓 asp.net core 可以自身作到更加的模塊化,目前這塊的內容還不夠成熟。緣由是缺乏組件信息和內容。
若是你想用於生產環境建議你可使用ABP,若是你勇於嘗試,敢於創新的話能夠直接使用abp vNext進行開發的。
abp vNext官網:https://abp.io/
github:https://github.com/abpframework/abp
文檔:https://abp.io/documentssql
Quartz.NET是一個強大、開源、輕量的做業調度框架,你可以用它來爲執行一個做業而建立簡單的或複雜的做業調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like表達式等等。目前已經正式支持了.NET Core 和async/await。
說白了就是你可使用Quartz.NET能夠很方便的開發定時任務諸如平時的工做中,定時輪詢數據庫同步,定時郵件通知,定時處理數據等。數據庫
這一節咱們經過實例進行操做,相信跟着作的你也可以把代碼跑起來。json
既然咱們這次演練的項目是使用的abp vNext這個asp.net core的快速開發框架來完成的,因此首先在項目開始以前,你須要到ABP vNext的官網上去下載項目代碼。英文站打開慢的話,能夠訪問中文子域名進行訪問:https://cn.abp.io/Templates 。下面給出具體步驟:c#
打開https://cn.abp.io/Templates 而後如圖填寫對應的項目名稱,這裏我用的Czar.AbpDemo
項目類型選擇ASP.NET Core MVC應用程序,由於這個是帶有UI界面的web項目,數據庫提供程序選擇EFCore這個你們都比較熟悉,而後點擊建立就能夠了。微信
下載後,解壓到一個文件夾下面,而後用vs打開解決方案,看到以下圖所示的項目結構架構
這裏簡單介紹下,每一個項目的做用,具體的就不過多介紹了,在下面的實戰代碼中慢慢體會吧
.Domain
爲領域層..Application
爲應用層..Web
爲是表示層..EntityFrameworkCore
是EF Core集成.解決方案還包含配置好的的單元&集成測試項目, 以便與於EF Core 和 SQLite 數據庫配合使用.
查看.Web
項目下appsettings.json
文件中的 鏈接字符串並進行相應的修改,怎麼改不要問我:
{ "ConnectionStrings": { "Default": "Server=localhost;Database=CzarAbpDemo;Trusted_Connection=True;MultipleActiveResultSets=true" } }
右鍵單擊.Web
項目並將其設爲啓動項目
打開包管理器控制檯(Package Manager Console), 選擇.EntityFrameworkCore
項目做爲默認項目並運行Update-Database
命令:
如今能夠運行應用程序,它將會打開home頁面:
點擊「Login」 輸入用戶名admin
, 密碼1q2w3E*
, 登陸應用程序.
啓動模板包括 身份管理(identity management) 模塊. 登陸後將提供身份管理菜單,你能夠在其中管理角色,用戶及其權限. 這個不過多講解了,本身去動手操做一番吧
這部分咱們將實現Quartz.NET定時任務的管理功能,爲了進行Quartz.NET定時任務的管理,咱們還須要定義一個表來進行Quartz.NET定時任務的信息的承載,並完成這個表的增刪改查功能,這樣咱們在對這個表的數據進行操做的同時來進行Quartz.NET定時任務的操做便可實現咱們的需求。話很少說,開始吧。這部分咱們再分紅兩個小節:JobInfo的增刪改查功能的實現,Quartz.NET調度任務功能的增刪改查的實現。
這個部分你將體會到我爲何使用abp vNext框架來進行開發了,就是由於快~~~~
建立領域實體對象JobInfo,這個在領域層代碼以下:
將咱們的JobInfo實體添加到DBContext中,這樣應該在EF層
添加新的Migration並更新到數據庫中,這個應該算EFCore的基礎了吧,兩個步驟,一個「Add-Migration」 而後「Update-Database」更新到數據庫便可
Add-Migration "Add_JobInfo_Entity" Update-Database
應用層建立頁面顯示實體BookDto
用來在 基礎設施層 和 應用層 傳遞數據
一樣的你還須要在應用層建立一個用來傳遞增改的Dto對象
萬事俱備,只欠服務了,接下來咱們建立一下JobInfo
的服務接口以及服務接口的實現了,這裏有個約定,就是全部的服務AppService
結尾,就跟控制器都以Controller
結尾的概念差很少。
服務實現:
註釋還算清真,相信你應該能看懂。
這裏abp vNext框架就會自動爲咱們實現增刪改查的API Controllers接口的實現(能夠經過swagger進行查看),還會自動 爲全部的API接口建立了JavaScript 代理.所以,你能夠像調用 JavaScript function同樣調用任何接口.
以下圖所示
是否是,感受什麼都還沒作,全部接口都已經實現的感受。
新增一個菜單任務調度的菜單,以下代碼所示:
對應的,咱們須要在Pages/JobSchedule
這個路徑下面建立對應的Index.cshtml頁面,以及新增,編輯的頁面。因爲內容太多,這裏就不貼代碼了,只給你們貼下圖:
Index.cshtml
CreateModal.cshtml代碼以下:
而後咱們運行起來查看下:
點擊,右上角的新增,會彈出新增界面,點擊每一行的操做,會彈出刪除(刪除,這裏只作了一個假功能),編輯的兩個選項。
到此,JobInfo
的增刪改查就作好了,是否是很簡單,這就是abp vNext賦予咱們的高效之處。
在使用Quartz.NET以前,你須要經過Nuget進行下安裝,而後才能進行調用。這裏我不會給你詳細講解Quartz.NET的使用,由於這將佔用大量的篇幅,並偏離本文的主旨
安裝Quartz.NET的Nuget包:
新建一個ScheduleCenter
的任務調度中心,代碼以下所示:
/// <summary> /// 任務調度中心 /// </summary> public class ScheduleCenter { private readonly ILogger _logger; public ScheduleCenter(ILogger<ScheduleCenter> logger) { _logger = logger; } /// <summary> /// 任務計劃 /// </summary> public IScheduler scheduler = null; public async Task<IScheduler> GetSchedulerAsync() { if (scheduler != null) { return scheduler; } else { // 從Factory中獲取Scheduler實例 NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" }, //如下配置須要數據庫表配合使用,表結構sql地址:https://github.com/quartznet/quartznet/tree/master/database/tables //{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"}, //{ "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"}, //{ "quartz.jobStore.tablePrefix","QRTZ_"}, //{ "quartz.jobStore.dataSource","myDS"}, //{ "quartz.dataSource.myDS.connectionString",AppSettingHelper.MysqlConnection},//鏈接字符串 //{ "quartz.dataSource.myDS.provider","MySql"}, //{ "quartz.jobStore.usePropert ies","true"} }; StdSchedulerFactory factory = new StdSchedulerFactory(props); return await factory.GetScheduler(); } } /// <summary> /// 添加調度任務 /// </summary> /// <param name="jobName">任務名稱</param> /// <param name="jobGroup">任務分組</param> /// <returns></returns> public async Task<bool> AddJobAsync(CreateUpdateJobInfoDto infoDto) { try { if (infoDto!=null) { if (infoDto.StarTime == null) { infoDto.StarTime = DateTime.Now; } DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(infoDto.StarTime, 1); if (infoDto.EndTime == null) { infoDto.EndTime = DateTime.MaxValue.AddDays(-1); } DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(infoDto.EndTime, 1); scheduler = await GetSchedulerAsync(); JobKey jobKey = new JobKey(infoDto.JobName, infoDto.JobGroup); if (await scheduler.CheckExists(jobKey)) { await scheduler.PauseJob(jobKey); await scheduler.DeleteJob(jobKey); } IJobDetail job = JobBuilder.Create<LogTestJob>() .WithIdentity(jobKey) .Build(); ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create() .StartAt(starRunTime) .EndAt(endRunTime) .WithIdentity(infoDto.JobName, infoDto.JobGroup) .WithCronSchedule(infoDto.CronExpress) .Build(); await scheduler.ScheduleJob(job, trigger); await scheduler.Start(); return true; } return false;//JobInfo爲空 } catch (Exception ex) { _logger.LogException(ex); return false;//出現異常 } } /// <summary> /// 暫停指定任務計劃 /// </summary> /// <param name="jobName">任務名</param> /// <param name="jobGroup">任務分組</param> /// <returns></returns> public async Task<bool> StopJobAsync(string jobName, string jobGroup) { try { JobKey jobKey = new JobKey(jobName, jobGroup); scheduler = await GetSchedulerAsync(); if (await scheduler.CheckExists(jobKey)) { await scheduler.PauseJob(new JobKey(jobName, jobGroup)); return true; } else { return false;//任務不存在 } } catch (Exception ex) { _logger.LogException(ex); return false;//出現異常 } } /// <summary> /// 恢復指定的任務計劃,若是是程序奔潰後 或者是進程殺死後的恢復,此方法無效 /// </summary> /// <param name="jobName">任務名稱</param> /// <param name="jobGroup">任務組</param> /// <returns></returns> public async Task<bool> ResumeJobAsync(string jobName, string jobGroup) { try { JobKey jobKey = new JobKey(jobName, jobGroup); scheduler = await GetSchedulerAsync(); if (await scheduler.CheckExists(jobKey)) { //resumejob 恢復 await scheduler.ResumeJob(new JobKey(jobName, jobGroup)); return true; } else { return false;//不存在任務 } } catch (Exception ex) { _logger.LogException(ex); return false;//出現異常 } } /// <summary> /// 恢復指定的任務計劃,若是是程序奔潰後 或者是進程殺死後的恢復,此方法無效 /// </summary> /// <param name="jobName">任務名稱</param> /// <param name="jobGroup">任務組</param> /// <returns></returns> public async Task<bool> DeleteJobAsync(string jobName, string jobGroup) { try { JobKey jobKey = new JobKey(jobName, jobGroup); scheduler = await GetSchedulerAsync(); if (await scheduler.CheckExists(jobKey)) { //DeleteJob 恢復 await scheduler.DeleteJob(jobKey); return true; } else { return false;//不存在任務 } } catch (Exception ex) { _logger.LogException(ex); return false;//出現異常 } } }
新建一個LogTestJob
的計劃任務,代碼以下所示,須要繼承IJob
接口:
至此Quartz.NET調度任務功能完成
這裏咱們按照以前的思路對JobInfo
跟Quartz.NET任務進行集成
新增時,啓動任務:
編輯時,更新任務
這裏細心的網友,可能注意到任務的刪除是在編輯裏面進行實現的。而列表頁面的刪除功能並無實現真正意義的功能的刪除。
上面咱們演示的任務是一個每5秒寫入當前時間的一個任務,並實現了對這個任務的新增,刪除,編輯的功能,這裏大夥能夠自行實現進行測試,也能夠下載個人代碼進行嘗試。效果圖以下所示:
目前只能對既定義好任務進行調度,後期能夠根據任務的名稱,如咱們實例中的測試任務LogTestJob
的名字找到這個任務,而後動態的進行處理。這樣就能夠在界面實現對多個任務進行調度了!固然還有其餘的擴展,本文只是做爲引子。
GitHub:https://github.com/yilezhu/AbpQuzatzDemo
本文只是簡單的利用abp vNext框架進行Quartz.NET任務調度進行UI的管理,實現的功能也比較簡單,你們徹底能夠在此基礎上進行擴展完善,最後感謝大夥的閱讀。