今天這篇文章我將通過實例代碼帶著大家一步一步通過abp vNext這個asp.net core的快速開發框架來進行Quartz.net定時任務調度的管理界面的開發。大伙最好跟著一起敲一下代碼,當然源碼我會上傳到github上,有興趣的小伙伴可以在文章底部查看源碼鏈接。 作者:依樂祝 原文鏈接:htt ...
今天這篇文章我將通過實例代碼帶著大家一步一步通過abp vNext這個asp.net core的快速開發框架來進行Quartz.net定時任務調度的管理界面的開發。大伙最好跟著一起敲一下代碼,當然源碼我會上傳到github上,有興趣的小伙伴可以在文章底部查看源碼鏈接。
作者:依樂祝
原文鏈接:https://www.cnblogs.com/yilezhu/p/10444060.html
寫在前面
有幾天沒更新博客了,一方面因為比較忙,另一方面是因為最近在準備組織我們霸都合肥的.NET技術社區首次非正式的線下聚會,忙著聯繫人啊,這裡歡迎有興趣的小伙伴加我wx:jkingzhu進行詳細的瞭解,當然也歡迎同行加我微信,然後我拉你進入我們合肥.NET技術社區微信群跟大伙進行交流。
概念
開始之前還有必要跟大伙說一下abp vNext以及Quartz.net是什麼,防止有小白。如果對這兩個概念非常熟悉的話可以直接閱讀下一節。項目最終實現的效果如下圖所示:
abp vNext是什麼
說起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/documents
Quartz.NET是什麼
Quartz.NET是一個強大、開源、輕量的作業調度框架,你能夠用它來為執行一個作業而創建簡單的或複雜的作業調度。它有很多特征,如:資料庫支持,集群,插件,支持cron-like表達式等等。目前已經正式支持了.NET Core 和async/await。
說白了就是你可以使用Quartz.NET可以很方便的開發定時任務諸如平時的工作中,定時輪詢資料庫同步,定時郵件通知,定時處理數據等。
實例演練
這一節我們通過實例進行操作,相信跟著做的你也能夠把代碼跑起來。
ABP vNext代碼
既然我們此次演練的項目是使用的abp vNext這個asp.net core的快速開發框架來完成的,所以首先在項目開始之前,你需要到ABP vNext的官網上去下載項目代碼。英文站打開慢的話,可以訪問中文子功能變數名稱進行訪問:https://cn.abp.io/Templates 。下麵給出具體步驟:
打開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定時任務的信息的承載,並完成這個表的增刪改查功能,這樣我們在對這個表的數據進行操作的同時來進行Quartz.NET定時任務的操作即可實現我們的需求。話不多說,開始吧。這部分我們再分成兩個小節:JobInfo的增刪改查功能的實現,Quartz.NET調度任務功能的增刪改查的實現。
JobInfo的增刪改查功能的實現
這個部分你將體會到我為什麼使用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調度任務功能的增刪改的實現
在使用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的管理,實現的功能也比較簡單,大家完全可以在此基礎上進行擴展完善,最後感謝大伙的閱讀。