今天這篇文章我將透過實體程式碼帶著大家一步一步透過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開啟解決方案,看到如下圖所示的專案結構
-
這裡簡單介紹下,每個專案的作用,具體的就不過多介紹了,在下麵的實戰程式碼中慢慢體會吧
解決方案還包含配置好的的單元&整合測試專案, 以便與於EF Core 和 SQLite 資料庫配合使用.
.Domain
為領域層..Application
為應用層..Web
為是表示層..EntityFrameworkCore
是EF Core整合.-
檢視
.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
的任務排程中心,程式碼如下所示:/// /// 任務排程中心 ///
public class ScheduleCenter
{
private readonly ILogger _logger;
public ScheduleCenter(ILogger logger)
{
_logger = logger;
}
///
/// 任務計劃
///
public IScheduler scheduler = null;
public async Task 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();
}
}
///
/// 新增排程任務
///
/// 任務名稱
/// 任務分組
///
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()
.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;//出現異常
}
}
///
/// 暫停指定任務計劃
///
/// 任務名
/// 任務分組
///
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;//出現異常
}
}
///
/// 恢復指定的任務計劃,如果是程式奔潰後 或者是行程殺死後的恢復,此方法無效
///
/// 任務名稱
/// 任務組
///
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;//出現異常
}
}
///
/// 恢復指定的任務計劃,如果是程式奔潰後 或者是行程殺死後的恢復,此方法無效
///
/// 任務名稱
/// 任務組
///
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的管理,實現的功能也比較簡單,大家完全可以在此基礎上進行擴充套件完善,最後感謝大夥的閱讀。