歡迎光臨
每天分享高質量文章

用abp vNext快速開發Quartz.NET定時任務管理界面

今天這篇文章我將通過實體代碼帶著大家一步一步通過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 。下麵給出具體步驟:

  1. 打開https://cn.abp.io/Templates 然後如圖填寫對應的專案名稱,這裡我用的Czar.AbpDemo 專案型別選擇ASP.NET Core MVC應用程式,因為這個是帶有UI界面的web專案,資料庫提供程式選擇EFCore這個大家都比較熟悉,然後點擊創建就可以了。

  2. 下載後,解壓到一個檔案夾下麵,然後用vs打開解決方案,看到如下圖所示的專案結構

  3. 這裡簡單介紹下,每個專案的作用,具體的就不過多介紹了,在下麵的實戰代碼中慢慢體會吧

    解決方案還包含配置好的的單元&集成測試專案, 以便與於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框架來進行開發了,就是因為快~~~~

  1. 創建領域物體物件JobInfo,這個在領域層代碼如下:

  2. 將我們的JobInfo物體添加到DBContext中,這樣應該在EF層

  3. 添加新的Migration並更新到資料庫中,這個應該算EFCore的基礎了吧,兩個步驟,一個“Add-Migration” 然後“Update-Database”更新到資料庫即可

    Add-Migration "Add_JobInfo_Entity"
    Update-Database
  4. 應用層創建頁面顯示物體BookDto 用來在 基礎設施層 和 應用層 傳遞資料

  5. 同樣的你還需要在應用層創建一個用來傳遞增改的Dto物件

  6. 萬事俱備,只欠服務了,接下來我們創建一下JobInfo的服務接口以及服務接口的實現了,這裡有個約定,就是所有的服務AppService結尾,就跟控制器都以Controller結尾的概念差不多。

    服務實現:

    註釋還算清真,相信你應該能看懂。

  7. 這裡abp vNext框架就會自動為我們實現增刪改查的API Controllers接口的實現(可以通過swagger進行查看),還會自動 為所有的API接口創建了JavaScript 代理.因此,你可以像呼叫 JavaScript function一樣呼叫任何接口.

    如下圖所示


    是不是,感覺什麼都還沒做,所有接口都已經實現的感覺。

  8. 新增一個選單任務調度的選單,如下代碼所示:

  9. 對應的,我們需要在Pages/JobSchedule 這個路徑下麵創建對應的Index.cshtml頁面,以及新增,編輯的頁面。由於內容太多,這裡就不貼代碼了,只給大家貼下圖:

    Index.cshtml

    CreateModal.cshtml代碼如下:

  10. 然後我們運行起來查看下:

  11. 點擊,右上角的新增,會彈出新增界面,點擊每一行的操作,會彈出刪除(刪除,這裡只做了一個假功能),編輯的兩個選項。

  12. 到此,JobInfo的增刪改查就做好了,是不是很簡單,這就是abp vNext賦予我們的高效之處。

Quartz.NET調度任務功能的增刪改的實現

在使用Quartz.NET之前,你需要通過Nuget進行下安裝,然後才能進行呼叫。這裡我不會給你詳細講解Quartz.NET的使用,因為這將占用大量的篇幅,並偏離本文的主旨

  1. 安裝Quartz.NET的Nuget包:

  2. 新建一個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任務進行集成

  1. 新增時,啟動任務:

  2. 編輯時,更新任務

  3. 這裡細心的網友,可能註意到任務的刪除是在編輯裡面進行實現的。而串列頁面的刪除功能並沒有實現真正意義的功能的刪除。

功能演示

上面我們演示的任務是一個每5秒寫入當前時間的一個任務,並實現了對這個任務的新增,刪除,編輯的功能,這裡大伙可以自行實現進行測試,也可以下載我的代碼進行嘗試。效果圖如下所示:

功能擴展

目前只能對既定義好任務進行調度,後期可以根據任務的名稱,如我們實體中的測試任務LogTestJob 的名字找到這個任務,然後動態的進行處理。這樣就可以在界面實現對多個任務進行調度了!當然還有其他的擴展,本文只是作為引子。

原始碼地址

GitHub:https://github.com/yilezhu/AbpQuzatzDemo

總結

本文只是簡單的利用abp vNext框架進行Quartz.NET任務調度進行UI的管理,實現的功能也比較簡單,大家完全可以在此基礎上進行擴展完善,最後感謝大伙的閱讀。

 

    赞(0)

    分享創造快樂