Asp.Net Core 使用Quartz基於界面畫介面管理做定時任務 ...
今天抽出一點點時間來造一個小輪子,是關於定時任務這塊的。
這篇文章主要從一下幾點介紹:
- 創建資料庫管理表
- 創建web項目
- 引入quarzt nuget 包
- 寫具體配置操作,實現定時任務處理
第一步:創建一個空web項目,引入quarzt nuget 包
創建TB.AspNetCore.Quartz web項目和TB.AspNetCore.Data 類庫,在web項目中引入Quartz nuget包
第二部:資料庫創建一張管理表
-- ---------------------------- -- Table structure for ScheduleInfo -- ---------------------------- DROP TABLE IF EXISTS `ScheduleInfo`; CREATE TABLE `ScheduleInfo` ( `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `JobGroup` varchar(100) NOT NULL DEFAULT '' COMMENT '任務組', `JobName` varchar(50) NOT NULL DEFAULT '' COMMENT '任務名', `RunStatus` int(11) NOT NULL DEFAULT '0' COMMENT '運行狀態', `CromExpress` varchar(40) NOT NULL DEFAULT '' COMMENT 'Crom表達式', `StarRunTime` datetime DEFAULT NULL COMMENT '開始運行時間', `EndRunTime` datetime DEFAULT NULL COMMENT '結束運行時間', `NextRunTime` datetime DEFAULT NULL COMMENT '下次運行時間', `Token` varchar(40) NOT NULL DEFAULT '' COMMENT 'Token', `AppID` varchar(40) NOT NULL DEFAULT '' COMMENT 'AppID', `ServiceCode` varchar(40) DEFAULT NULL, `InterfaceCode` varchar(40) DEFAULT NULL, `TaskDescription` varchar(200) DEFAULT NULL, `DataStatus` int(11) DEFAULT NULL COMMENT '數據狀態', `CreateAuthr` varchar(30) DEFAULT NULL COMMENT '創建人', `CreateTime` datetime DEFAULT NULL COMMENT '創建時間', PRIMARY KEY (`Id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
創建資料庫的表結構如圖所示,接下來我們在Data項目里添加mysql資料庫驅動nuget包
打開PM,執行資料庫反向工程命令,從資料庫生成model實體
Scaffold-DbContext "Server=你的伺服器地址;Database=你的資料庫;User=資料庫用戶名;Password=你的資料庫密碼;" "Pomelo.EntityFrameworkCore.MySql" -OutputDir Entity**2.1.1 建議不要選用,防坑!
將鏈接字元串換成你自己的,我們又新添加了一個service文件夾和一個Enum文件夾
其中,BaseService 里封裝了針對數據操作的基本crud,quartz里封裝了關於定時任務的配置,enum里枚舉了任務狀態,具體代碼如下
public enum JobStatus { [Description("已啟用")] 已啟用, [Description("運行中")] 待運行, [Description("執行中")] 執行中, [Description("執行完成")] 執行完成, [Description("執行任務計劃中")] 執行任務計劃中, [Description("已停止")] 已停止, }
下麵是baseservice里具體方法
public class BaseService { protected static object obj = new object(); public ggb_offlinebetaContext _context; protected ggb_offlinebetaContext DataContext { get { if (_context == null) { _context = new ggb_offlinebetaContext(); } return _context; } } public BaseService() { } #region 封裝基crud /// <summary> /// 只能是唯一記錄 多記錄引發異常 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="predicate"></param> /// <returns></returns> public TSource Single<TSource>(Expression<Func<TSource, bool>> predicate = null) where TSource : class { if (predicate == null) { return this.DataContext.Set<TSource>().SingleOrDefault(); } return this.DataContext.Set<TSource>().SingleOrDefault(predicate); } /// <summary> /// 查詢一條記錄 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="predicate"></param> /// <returns></returns> public TSource First<TSource>(Expression<Func<TSource, bool>> predicate = null) where TSource : class { if (predicate == null) { return this.DataContext.Set<TSource>().FirstOrDefault(); } return this.DataContext.Set<TSource>().FirstOrDefault(predicate); } /// <summary> /// where條件查詢 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="predicate"></param> /// <returns></returns> public IQueryable<TSource> Where<TSource>(Expression<Func<TSource, bool>> predicate = null) where TSource : class { if (predicate == null) { return this.DataContext.Set<TSource>().AsQueryable(); } return this.DataContext.Set<TSource>().Where(predicate); } /// <summary> /// 記錄數 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="predicate"></param> /// <returns></returns> public int Count<TSource>(Expression<Func<TSource, bool>> predicate = null) where TSource : class { if (predicate == null) { return this.DataContext.Set<TSource>().Count(); } return this.DataContext.Set<TSource>().Count(predicate); } /// <summary> /// 根據條件判斷記錄是否存在 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="predicate"></param> /// <returns></returns> /// Any確定序列是否包含任何元素 public bool Exists<TSource>(Expression<Func<TSource, bool>> predicate = null) where TSource : class { if (predicate == null) { return this.DataContext.Set<TSource>().Any(); } return this.DataContext.Set<TSource>().Any(predicate); } /// <summary> /// 查詢全部 /// </summary> /// <typeparam name="TSource"></typeparam> /// <returns></returns> public IQueryable<TSource> Query<TSource>() where TSource : class { return this.DataContext.Set<TSource>(); } /// <summary> /// paging the query 分頁查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="query"></param> /// <param name="pageIndex">page index</param> /// <param name="pageSize">page size </param> /// <param name="count">total row record count</param> /// <returns></returns> public IQueryable<T> Pages<T>(IQueryable<T> query, int pageIndex, int pageSize, out int count) where T : class { if (pageIndex < 1) { pageIndex = 1; } if (pageSize < 1) { pageSize = 10; } count = query.Count(); query = query.Skip((pageIndex - 1) * pageSize).Take(pageSize); return query; } /// <summary> /// 分頁查詢 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <param name="count"></param> /// <returns></returns> public IQueryable<T> Pages<T>(int pageIndex, int pageSize, out int count) where T : class { if (pageIndex < 1) { pageIndex = 1; } if (pageSize < 1) { pageSize = 10; } var query = this.DataContext.Set<T>().AsQueryable(); count = query.Count(); query = query.Skip((pageIndex - 1) * pageSize).Take(pageSize); return query; } #endregion /// <summary> /// 做一次提交 /// </summary> #region Save Changes public void Save() { //todo 需要驗證是否需要釋放 using (this.DataContext) { this.DataContext.SaveChanges(); } } /// <summary> /// 添加 /// </summary> /// <param name="entity"></param> /// <param name="save"></param> public void Add(object entity, bool save = false) { this.DataContext.Add(entity); if (save) { this.Save(); } } /// <summary> /// 更新實體 /// </summary> /// <param name="entity"></param> /// <param name="save"></param> public void Update(object entity, bool save = false) { this.DataContext.Update(entity); if (save) { this.Save(); } } /// <summary> /// 更新2 /// </summary> /// <param name="list"></param> /// <param name="save"></param> public void Update(IEnumerable<object> list, bool save = false) { this.DataContext.UpdateRange(list); if (save) { this.Save(); } } /// <summary> /// 刪除1 /// </summary> /// <param name="entity"></param> /// <param name="save"></param> public void Delete(object entity, bool save = false) { this.DataContext.Remove(entity); if (save) { this.Save(); } } /// <summary> /// 刪除2 /// </summary> /// <param name="list"></param> /// <param name="save"></param> public void Delete(IEnumerable<object> list, bool save = false) { this.DataContext.RemoveRange(list); if (save) { this.Save(); } } #endregion ///// <summary> ///// 釋放資源 ///// </summary> //public void Dispose() //{ // _context.Dispose(); //} }
下麵是任務調度中心代碼
/// <summary> /// 任務調度中心 /// </summary> public class JobCenter { /// <summary> /// 任務計劃 /// </summary> public static IScheduler scheduler = null; public static async Task<IScheduler> GetSchedulerAsync() { if (scheduler != null) { return scheduler; } else { ISchedulerFactory schedf = new StdSchedulerFactory(); IScheduler sched = await schedf.GetScheduler(); return sched; } } /// <summary> /// 添加任務計劃//或者進程終止後的開啟 /// </summary> /// <returns></returns> public async Task<bool> AddScheduleJobAsync(ScheduleInfo m) { try { if (m != null) { if (m.StarRunTime == null) { m.StarRunTime = DateTime.Now; } DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(m.StarRunTime, 1); if (m.EndRunTime == null) { m.EndRunTime = DateTime.MaxValue.AddDays(-1); } DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(m.EndRunTime, 1); scheduler = await GetSchedulerAsync(); IJobDetail job = JobBuilder.Create<HttpJob>() .WithIdentity(m.JobName, m.JobGroup) .Build(); ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create() .StartAt(starRunTime) .EndAt(endRunTime) .WithIdentity(m.JobName, m.JobGroup) .WithCronSchedule(m.CromExpress) .Build(); //將信息寫入 new ScheduleManage().AddSchedule(m); await scheduler.ScheduleJob(job, trigger); await scheduler.Start(); await StopScheduleJobAsync(m.JobGroup, m.JobName); return true; } return false; } catch (Exception ex) { //MyLogger.WriteError(ex, null); return false; } } /// <summary> /// 暫停指定任務計劃 /// </summary> /// <returns></returns> public async Task<string> StopScheduleJobAsync(string jobGroup, string jobName) { try { scheduler = await GetSchedulerAsync(); //使任務暫停 await scheduler.PauseJob(new JobKey(jobName, jobGroup)); //更新資料庫 new ScheduleManage().UpdateScheduleStatus(new ScheduleInfo() { JobName = jobName, JobGroup = jobGroup, RunStatus = (int)JobStatus.已停止 }); var status = new StatusViewModel() { Status = 0, Msg = "暫停任務計劃成功", }; return JsonConvert.SerializeObject(status); } catch (Exception ex) { //MyLogger.WriteError(ex, null); var status = new StatusViewModel() { Status = -1, Msg = "暫停任務計劃失敗", }; return JsonConvert.SerializeObject(status); } } /// <summary> /// 恢復指定的任務計劃**恢復的是暫停後的任務計劃,如果是程式奔潰後 或者是進程殺死後的恢復,此方法無效 /// </summary> /// <returns></returns> public async Task<string> RunScheduleJobAsync(string jobGroup, string jobName) { try { //獲取model var sm = new ScheduleManage().GetScheduleModel(new ScheduleInfo() { JobName = jobName, JobGroup = jobGroup }); await AddScheduleJobAsync(sm); sm.RunStatus = (int)JobStatus.已啟用; //更新model new ScheduleManage().UpdateScheduleStatus(sm); scheduler = await GetSchedulerAsync(); //resumejob 恢復 await scheduler.ResumeJob(new JobKey(jobName, jobGroup)); var status = new StatusViewModel() { Status = 0, Msg = "開啟任務計劃成功", }; return JsonConvert.SerializeObject(status); } catch (Exception ex) { var status = new StatusViewModel() { Status = -1, Msg = "開啟任務計劃失敗", }; return JsonConvert.SerializeObject(status); } } }
其他幾個文件的細節代碼我就不再粘貼,詳細代碼會推到github上去,接下來寫一個控制器看看效果!
第四部:項目運行截圖
具體詳細的東西,也沒有說的十分清晰,具體的代碼可以到github上去查看,
項目github地址:https://github.com/TopGuo/TB.AspNetCore.Quarzt
如果您認為這篇文章還不錯或者有所收穫,您可以點擊右下角的【推薦】按鈕精神支持,因為這種支持是我繼續寫作,分享的最大動力
歡迎大家關註我都我的微信 公眾號,公眾號漲粉絲人數,就是你們對我的喜愛程度!