前言:想在.net framework環境使用自定義定時器的話,參考我的另一篇文章:https://www.cnblogs.com/lxhbky/p/10242839.html 想在.net core中使用定時器功能,需要藉助一個服務介面:IHostedService, 繼承並實現對應方法,最後再s ...
前言:想在.net framework環境使用自定義定時器的話,參考我的另一篇文章:https://www.cnblogs.com/lxhbky/p/10242839.html
想在.net core中使用定時器功能,需要藉助一個服務介面:IHostedService, 繼承並實現對應方法,最後再setup.cs類中添加註冊服務:services.AddHostedService<實現服務類>(); 既然要寫計時器的伺服器,那麼該實現類就要包含定時器,本篇博客也是藉助System.Timers.Timer類封裝的。
下麵展示具體代碼:
1-公用基類:
public class ModelBase { protected IServiceProvider Services { get; set; } protected IWebHostEnvironment WebHostEnvironment { get; set; } /// <summary> /// 配置幫助類 /// </summary> protected ConfigHelper ConfigHelper { get; set; } /// <summary> /// 等同於ASP.NET裡面的WebCache(HttpRuntime.Cache) /// </summary> protected IMemoryCache MemoryCache { get; set; } /// <summary> /// 日誌 /// </summary> protected ILogger Logger { get; set; } /// <summary> /// 授權幫助 /// </summary> protected OAuthHelper OAuthHelper { get; set; } /// <summary> /// HttpClient幫助工廠 /// </summary> protected IHttpClientFactory HttpClientFactory { get; set; } public ModelBase(params object[] @params) { foreach (var item in @params) { if (item is IServiceProvider) { this.Services = (IServiceProvider)item; } else if (item is IWebHostEnvironment) { this.WebHostEnvironment = (IWebHostEnvironment)item; } else if (item is ConfigHelper) { this.ConfigHelper = (ConfigHelper)item; } else if (item is IMemoryCache) { this.MemoryCache = (IMemoryCache)item; } else if (item is ILogger) { this.Logger = (ILogger)item; } else if (item is OAuthHelper) { this.OAuthHelper = (OAuthHelper)item; } else if (item is IHttpClientFactory) { this.HttpClientFactory = (IHttpClientFactory)item; } } } }
2-計時器封裝類:
相對於.net framework文章計時器部分的類做了對應優化,更加簡化了:
using Microsoft.Extensions.Logging; using PaymentAccountAPI.Common; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Timers; namespace PaymentAccountAPI.Helper { /// <summary> /// 定時周期幫助類 /// </summary> public class TimeCycleHelp : ModelBase { public TimeCycleHelp(ILogger<TimeCycleHelp> logger) : base(logger) { this.Timer = new System.Timers.Timer(); } /// <summary> /// 服務專屬計時器 /// </summary> private System.Timers.Timer Timer; /// <summary> /// 預設計時器時間間隔1秒(提高計時器開始時間準確度) /// </summary> private double DefaultTimerInterval = 1 * 1000; /// <summary> /// 設置多個迴圈周期 /// </summary> private List<TimeCycle> TimeCycleList { get; set; } /// <summary> /// 更新一個計時器的計時周期 /// </summary> /// <param name="newTimerInterval">新的計時周期</param> /// <param name="isFirstStart">是否是首次更新計時器周期</param> private void UpdateTimeInterval(double newTimerInterval, bool isFirstStart = false) { if (this.Timer != null && newTimerInterval > 0) { this.Timer.Stop(); if (this.Timer.Interval != newTimerInterval) { this.Timer.Interval = newTimerInterval; } if (isFirstStart) { this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ServiceAction); } this.Timer.AutoReset = true; this.Timer.Start(); } } /// <summary> /// 內部輔助方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ServiceAction(object sender, ElapsedEventArgs e) { List<TimeCycle> currentTimeCycleList = new List<TimeCycle>(0); DateTime now = DateTime.Now; DateTime cycleBeginTime; DateTime cycleEndTime; foreach (TimeCycle timeCycle in this.TimeCycleList) { cycleBeginTime = Convert.ToDateTime(timeCycle.BeginTime); cycleBeginTime = now.Date.AddHours(cycleBeginTime.Hour).AddMinutes(cycleBeginTime.Minute).AddSeconds(cycleBeginTime.Second); cycleEndTime = Convert.ToDateTime(timeCycle.EndTime); cycleEndTime = now.Date.AddHours(cycleEndTime.Hour).AddMinutes(cycleEndTime.Minute).AddSeconds(cycleEndTime.Second); if (cycleEndTime < cycleBeginTime) { cycleEndTime = cycleEndTime.AddDays(1); } if (now >= cycleBeginTime && now <= cycleEndTime) { //有最大執行次數限制或者沒有限制 if (timeCycle.ActionExecutionTimes < timeCycle.MaxActionTimes || timeCycle.MaxActionTimes == 0) { TimeSpan timeSpan = now - cycleBeginTime; bool isCanAction = (int)timeSpan.TotalSeconds % timeCycle.ActionSeconds == 0 ? true : false; if (isCanAction) { timeCycle.ActionExecutionTimes++; currentTimeCycleList.Add(timeCycle); } } } else { //不在計時周期內,已執行次數清零 timeCycle.ActionExecutionTimes = 0; } } //找到當前迴圈周期後,執行周期內動作 if (currentTimeCycleList.Count > 0) { currentTimeCycleList.ForEach(item => { //使用多線程執行任務,讓代碼快速執行 Task.Run(() => item.Action()); }); } } /// <summary> /// 開啟計時器 /// </summary> /// <param name="timeCycleArray"></param> public void Start(params TimeCycle[] timeCycleArray) { if (timeCycleArray != null && timeCycleArray.Length > 0) { if (this.TimeCycleList == null) { this.TimeCycleList = new List<TimeCycle>(100); } this.TimeCycleList = timeCycleArray.ToList(); //設置首次計時器周期(首次動作執行,是在計時器啟動後在設置的時間間隔後做出的動作) this.UpdateTimeInterval(this.DefaultTimerInterval, true); } } /// <summary> /// 結束計時器 /// </summary> public void Stop() { this.Timer.Stop(); } } /// <summary> /// 計時周期類 /// </summary> public class TimeCycle { /// <summary> /// 唯一標識 /// </summary> public int ID { get; set; } /// <summary> /// 開始時間(誤差1秒=取決於計時器預設時間間隔) /// </summary> public string BeginTime { get; set; } /// <summary> /// 結束時間 /// </summary> public string EndTime { get; set; } /// <summary> /// 最大執行次數 /// </summary> public int MaxActionTimes { get; set; } /// <summary> /// 計時周期內執行的動作(動作會在到達開始時間後的) /// </summary> public Action Action { get; set; } /// <summary> /// 動作執行時間間隔(秒) /// </summary> public int ActionSeconds { get; set; } /// <summary> /// 方法執行次數 /// </summary> internal int ActionExecutionTimes { get; set; } public TimeCycle(int id, Action action, int actionSeconds) : this(id, "00:00:00", action, actionSeconds) { } public TimeCycle(int id, string beginTime, Action action, int actionSeconds) : this(id, beginTime, action, actionSeconds, 0) { } public TimeCycle(int id, string beginTime, Action action, int actionSeconds, int maxActionTimes) : this(id, beginTime, "23:59:59", action, actionSeconds, maxActionTimes) { } /// <summary> /// 基本構造器 /// </summary> /// <param name="id">唯一標識</param> /// <param name="beginTime">開始時間</param> /// <param name="endTime">結束時間</param> /// <param name="action">要執行的任務</param> /// <param name="actionSeconds">任務執行時間間隔</param> /// <param name="maxActionTimes">最大執行次數</param> public TimeCycle(int id, string beginTime, string endTime, Action action, int actionSeconds, int maxActionTimes) { this.ID = id; this.BeginTime = beginTime; this.EndTime = endTime; this.Action = action; this.ActionSeconds = actionSeconds; this.MaxActionTimes = maxActionTimes; } } }
3-webAPI服務封裝類:
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using PaymentAccountAPI.Common; using PaymentAccountAPI.Helper; using PaymentAccountAPI.PaymentAccountHelper; using System; using System.Threading; using System.Threading.Tasks; namespace PaymentAccountAPI.Hangfire { public class SyncToCMSService : ModelBase, IHostedService {private TimeCycleHelp _TimeCycleHelp { get; set; } public SyncToCMSService(ConfigHelper configHelper, TimeCycleHelp timeCycleHelp, ILogger<SyncToCMSService> logger) : base(logger, configHelper) {this._TimeCycleHelp = timeCycleHelp; } public void SyncData() { //...需要執行的任務代碼 this.Logger.LogInformation($"定時任務:完成{currentDate.ToShortDateString()}的交易記錄同步!"); } public Task StartAsync(CancellationToken cancellationToken) { this.Logger.LogInformation($"定時任務:同步服務已啟動..."); //正式代碼 string syncBeginTime = this.ConfigHelper.GetAppSettingValue("SyncCMSBeginTime"); string syncEndTime = this.ConfigHelper.GetAppSettingValue("SyncCMSEndTime"); this._TimeCycleHelp.Start(new TimeCycle(999, syncBeginTime, syncEndTime, this.SyncData, 3600, 2)); //測試代碼 //this._TimeCycleHelp.Start(new TimeCycle(999, "12:00:00", () => //{ // this.Logger.LogInformation("test123"); //}, 10, 3)); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { this.Logger.LogInformation($"同步服務已停止..."); this._TimeCycleHelp.Stop(); return Task.CompletedTask; } } }
4-在startup.cs添加註冊服務:
#region 添加定時任務 services.AddSingleton<TimeCycleHelp>(); services.AddHostedService<SyncToCMSService>(); #endregion
最後感謝一篇道友的文章:.Net Core 簡單定時任務框架封裝