一个简单易用的asp.net mvc 定时任务

时间:2022-08-29 16:51:55

因为项目中需要每月/每天建表,每天进行一些统计,所以封装了一个类库进行使用。

 

 一、使用说明:

1. 把要执行的任务新建一个类放到本Web项目中,可以放在任意位置,建议放在AutoTask 文件夹下
2. 对该类应用属性 AutoTask 如下所示
[AutoTask(EnterMethod = "StartTask", IntervalSeconds = 86400, StartTime = "2016-12-28 00:00:00")]
    说明:EnterMethod 是任务入口,IntervalSeconds 是执行间隔,StartTime 是开始执行时间
        如果设置IntervalSeconds 为空或0 ,则该任务只启动时执行一次,忽略后面的start Time 参数
        如果没有设置StartTime ,则该任务启动时立刻开始执行,按照间隔时间,每隔IntervalSeconds 秒执行一次。
        如果StartTime 大于当前日期,则任务从StartTime 开始执行。
        如果StartTime 小于当前日期,则从 StartTime + IntervalSecondes*n 开始执行,n为使该表达式大于DateTime.Now 的最小值。

3. 由此可以实现所有的定时功能。
     如指定 每小时执行,则设置IntervalSeconds=3600,每天执行则设置IntervalSeconds=86400
     若设置每天 0 点执行,则可以设置StartTime = "xxxx-xx-xx 00:00:00",IntervalSeconds=86400
     若设置每周二晚上23 点执行 ,则可以设置StartTime = "xxxx-xx-xx 23:00:00",IntervalSeconds=604800   //86400*7 ,xxxx-xx-xx 可以设置为以前一个周二的日期
     若设置每月,每年指定日期,则可以设置每天执行,然后在执行的代码里进行判断,判断日期是否是指定的日期,为保证易用性,本类库不进行扩展

 

二、源码说明:

 

1. 在Global.asax.cs Application_Start中添加如下代码

 AutoTaskAttribute.RegisterTask();

     添加Application_End 方法,解决IIS应用程序池自动回收的问题

   protected void Application_End(object sender, EventArgs e)
   {
            //下面的代码是关键,可解决IIS应用程序池自动回收的问题 
            Thread.Sleep(1000);
            //这里设置你的web地址,可以随便指向你的任意一个aspx页面甚至不存在的页面,目的是要激发Application_Start 
       //我这里是一个验证token的地址。 string url = System.Configuration.ConfigurationManager.AppSettings["tokenurl"]; HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); Stream receiveStream = myHttpWebResponse.GetResponseStream();//得到回写的字节流 }

 

2. 把该类文件添加到项目中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
using WebMVC.Utils;

namespace WebMVC.Filters
{
    /// <summary>
    /// Author:BigLiang(lmw)
    /// Date:2016-12-29
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class AutoTaskAttribute : Attribute
    {
        /// <summary>
        /// 入口程序
        /// </summary>
        public string EnterMethod { get; set; }
        /// <summary>
        /// 执行间隔秒数(未设置或0 则只执行一次)
        /// </summary>
        public int IntervalSeconds { get; set; }
        /// <summary>
        /// 开始执行日期
        /// </summary>
        public string StartTime { get; set; }

        //保留对Timer 的引用,避免回收
        private static Dictionary<AutoTaskAttribute, System.Threading.Timer> timers = new Dictionary<AutoTaskAttribute, System.Threading.Timer>();
       /// <summary>
       /// Global.asax.cs 中调用
       /// </summary>
        public static void RegisterTask()
        {
            new Task(() => StartAutoTask()).Start();
        }
        /// <summary>
        /// 启动定时任务
        /// </summary>
        private static void StartAutoTask()
        {
            var types = Assembly.GetExecutingAssembly().ExportedTypes.Where(t => Attribute.IsDefined(t, typeof(AutoTaskAttribute))).ToList();
            foreach (var t in types)
            {
                try
                {
                    var att = (AutoTaskAttribute)Attribute.GetCustomAttribute(t, typeof(AutoTaskAttribute));
                    if (att != null)
                    {
                        if (string.IsNullOrWhiteSpace(att.EnterMethod))
                        {
                            throw new Exception("未指定任务入口!EnterMethod");
                        }
                        var ins = Activator.CreateInstance(t);
                        var method = t.GetMethod(att.EnterMethod);

                        if (att.IntervalSeconds > 0)
                        {
                            int duetime = 0; //计算延时时间

                            if (string.IsNullOrWhiteSpace(att.StartTime))
                            {
                                duetime = 1000;
                            }
                            else
                            {
                                var datetime = DateTime.Parse(att.StartTime);
                                if (DateTime.Now <= datetime)
                                {
                                    duetime = (int)(datetime - DateTime.Now).TotalSeconds * 1000;
                                }
                                else
                                {
                                    duetime = att.IntervalSeconds * 1000 - ((int)(DateTime.Now - datetime).TotalMilliseconds) % (att.IntervalSeconds * 1000);
                                }
                            }

                            timers.Add(att,new System.Threading.Timer((o) =>
                            {
                                method.Invoke(ins, null);
                            }, ins, duetime, att.IntervalSeconds * 1000)); 
                        }
                        else
                        {
                            method.Invoke(ins, null);
                        }
                    }

                }
                catch (Exception ex)
                {
                    //LogHelper.Error(t.FullName + " 任务启动失败", ex);
                }
            }
        }

    }
}

 

 3. 使用示例

 /// <summary>
    /// 测试任务,每10分钟执行一次
    /// </summary>
    [AutoTask(EnterMethod = "StartTask", IntervalSeconds = 600, StartTime = "2016-12-28 10:45:00")]
    public class TestTask
    {

        /// <summary>
        ////// </summary>

        public static void StartTask()
        {
            LogHelper.Info("定时任务启动成功!");
            LogHelper.Info("定时任务执行完毕!");
        }
    }

 

这是我的框架中关于定时任务的一块,以后会把整个框架分享出来。