想在.net core中使用定时器功能,需要借助一个服务接口:IHostedService,   继承并实现对应方法,最后再setup.cs类中添加注册服务:services.AddHostedService

 

  下面展示具体代码:

  1-公用基类:

  

public class ModelBase
    {        protected IServiceProvider Services { get; set; }        protected IWebHostEnvironment WebHostEnvironment { get; set; }        /// 
        /// 配置帮助类        /// 
        protected ConfigHelper ConfigHelper { get; set; }        /// 
        /// 等同于ASP.NET里面的WebCache(HttpRuntime.Cache)        /// 
        protected IMemoryCache MemoryCache { get; set; }        /// 
        /// 日志        /// 
        protected ILogger Logger { get; set; }        /// 
        /// 授权帮助        /// 
        protected OAuthHelper OAuthHelper { get; set; }        /// 
        /// HttpClient帮助工厂        /// 
        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
{    /// 
    /// 定时周期帮助类    /// 
    public class TimeCycleHelp : ModelBase
    {        public TimeCycleHelp(ILoggerlogger) : base(logger)
        {            this.Timer = new System.Timers.Timer();
        }        /// 
        /// 服务专属计时器        /// 
        private System.Timers.Timer Timer;        /// 
        /// 默认计时器时间间隔1秒(提高计时器开始时间准确度)        /// 
        private double DefaultTimerInterval = 1 * 1000;        /// 
        /// 设置多个循环周期        /// 
        private ListTimeCycleList { get; set; }        /// 
        /// 更新一个计时器的计时周期        /// 
        /// 新的计时周期
        /// 是否是首次更新计时器周期
        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();
            }
        }        /// 
        /// 内部辅助方法        /// 
        /// 
        /// 
        private void ServiceAction(object sender, ElapsedEventArgs e)
        {
            ListcurrentTimeCycleList = new List(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());
                });
            }
        }        /// 
        /// 开启计时器        /// 
        /// 
        public void Start(params TimeCycle[] timeCycleArray)
        {            if (timeCycleArray != null && timeCycleArray.Length > 0)
            {                if (this.TimeCycleList == null)
                {                    this.TimeCycleList = new List(100);
                }                this.TimeCycleList = timeCycleArray.ToList();                //设置首次计时器周期(首次动作执行,是在计时器启动后在设置的时间间隔后做出的动作)
                this.UpdateTimeInterval(this.DefaultTimerInterval, true);
            }
        }        /// 
        /// 结束计时器        /// 
        public void Stop()
        {            this.Timer.Stop();
        }

    }    /// 
    /// 计时周期类    /// 
    public class TimeCycle
    {        /// 
        /// 唯一标识        /// 
        public int ID { get; set; }        /// 
        /// 开始时间(误差1秒=取决于计时器默认时间间隔)        /// 
        public string BeginTime { get; set; }        /// 
        /// 结束时间        /// 
        public string EndTime { get; set; }        /// 
        /// 最大执行次数        /// 
        public int MaxActionTimes { get; set; }        /// 
        /// 计时周期内执行的动作(动作会在到达开始时间后的)        /// 
        public Action Action { get; set; }        /// 
        /// 动作执行时间间隔(秒)        /// 
        public int ActionSeconds { get; set; }        /// 
        /// 方法执行次数        /// 
        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)
        {
        }        /// 
        /// 基本构造器        /// 
        /// 唯一标识
        /// 开始时间
        /// 结束时间
        /// 要执行的任务
        /// 任务执行时间间隔
        /// 最大执行次数
        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, ILoggerlogger) : 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();

            services.AddHostedService();            #endregion

 

  5-部署到IIS站点项目时的注意事项(引用下面道友的一段话):

       关于GenericHost的生存周期问题

  如果你使用的是控制台启动,则此问题暂时可以忽略。

  如果你使用的是站点项目,并且还是通过IIS启动,那么你可能要注意了,因为.net core 的站点自身是有HOST宿主处理,IIS是其上代理,其启动关闭,端口映射等由IIS内部完成。所以其依然受限于IIS的闲置回收影响,当IIS闲置回收时,其后的.Net Host也会被一同关闭,需要有新的请求进来时才会再次启动。不过鉴于当前任务处理已经如此简单,有个取巧的做法,实现一个站点自身的心跳检测任务,IIS默认20分钟回收,任务时间可以设为15分钟(你也可以设置IIS站点回收时间),当然如果你的任务如果没有那么严格的时间要求你也可以不用处理,因为回收后一旦接受到新的请求,任务会再次发起。