ASP.NET预定作业

时间:2013-11-28 06:42:39

标签: asp.net scheduled-tasks

我想在预定的时间内发送邮件和短信。我不喜欢Windows服务。 是否有任何选项可以在ASP.Net中的自定义时间内安排任务?

请建议哪种方式最好。

提前感谢。

5 个答案:

答案 0 :(得分:5)

当我的网站托管在Godaddy共享主机上时,我遇到了类似的问题。我想在没有任何Windows服务的情况下运行发送电子邮件的计划任务。

最后,我通过使用缓存解决了这个问题。

缓存项在给定时间或持续时间到期。在ASP.NET中,您可以在缓存中添加条目并设置绝对到期日期时间,也可以设置从缓存中删除项目的持续时间。您可以通过使用Cache类的以下方法来完成此操作。

    public void Insert ( System.String key , System.Object value , 
                     System.Web.Caching.CacheDependency dependencies , 
                     System.DateTime absoluteExpiration , 
                     System.TimeSpan slidingExpiration , 
                     System.Web.Caching.CacheItemPriority priority , 
                     System.Web.Caching.CacheItemRemovedCallback onRemoveCallback )

onRemoveCallback是一个方法的委托,只要缓存项到期就会调用该方法。在那种方法中,我们可以做任何我们喜欢的事情。因此,这是一个很好的候选者,可以定期运行代码,而不需要任何页面访问。

这意味着,我们可以使用缓存超时来模拟Windows服务。

您可以在codeproject网址http://www.codeproject.com/Articles/12117/Simulate-a-Windows-Service-using-ASP-NET-to-run-sc上找到完整的详细信息。

答案 1 :(得分:2)

计时器回调怎么样,在Application_Start中设置:

protected void Application_Start(object sender, EventArgs e)
{
    TimerCallback callback = (x) => { YourFunction(); };
    int intervalInMS = 2 * 60 * 60 * 1000;  // every 2 hours. 
    timer = new Timer(callback, state: null, dueTime: intervalInMS, period: intervalInMS);
}

答案 2 :(得分:1)

Quartz.Net(http://www.mikesdotnetting.com/article/254/scheduled-tasks-in-asp-net-with-quartz-net)是一个库,可以轻松设置和管理作为应用程序一部分的作业。

答案 3 :(得分:1)

Jeff Atwood提供..

在启动时(global.asx),将一个项目添加到HttpRuntime.Cache并且过期时间固定。

当缓存项目到期时,请完成您的工作,例如WebRequest或您有什么。

使用固定的到期时间将项目重新添加到缓存中。 代码很简单,真的:

private static CacheItemRemovedCallback OnCacheRemove = null;

protected void Application_Start(object sender, EventArgs e)
{
    AddTask("DoStuff", 60);
}

private void AddTask(string name, int seconds)
{
    OnCacheRemove = new CacheItemRemovedCallback(CacheItemRemoved);
    HttpRuntime.Cache.Insert(name, seconds, null, 
        DateTime.Now.AddSeconds(seconds), Cache.NoSlidingExpiration,
        CacheItemPriority.NotRemovable, OnCacheRemove);
}

public void CacheItemRemoved(string k, object v, CacheItemRemovedReason r)
{
    // do stuff here if it matches our taskname, like WebRequest
    // re-add our task so it recurs
    AddTask(k, Convert.ToInt32(v));
}

您的“DoStuff”是触发您运行所需的实际工作的方法。

就这么简单!

答案 4 :(得分:0)

我创建了一个类来封装此行为以用于Web服务器上的任务。它的灵感来自于这里和Jeff Atwood的博客文章中的答案,但我认为我会分享它,以防对其他人有用。

此配置可用于我的本地时区(CST),但可以很容易地适应另一个时区。

Application_Start中安排任务

此示例将TaskUpdateUserRoles.Task方法安排为每天CST凌晨3:00调用。将其放在Application_Start文件的Global.asax.cs方法中。

ServerTask.CreateDaily("Update User Roles",
    TaskUpdateUserRoles.Task,
    DateTime.Parse("3:00 AM").TimeOfDay); //This should be the local time. CST in this case.

ServerTask类

using System;
using System.Diagnostics;
using System.IO;
using System.Web;
using System.Web.Caching;

namespace MySite.Admin.Tasks
{
    /// <summary>
    /// Manages recurring tasks to run on the server.
    /// </summary>
    public class ServerTask
    {
        private string TaskName { get; set; }
        private TaskFrequency Frequency { get; set; }
        private TimeSpan Offset { get; set; }
        private Func<bool> TaskFunction { get; set; }
        private CacheItemRemovedCallback OnCacheRemove = null;
        private static readonly string TaskLogPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Admin/Logs/");

        private ServerTask(string name, TaskFrequency freq, TimeSpan offset, Func<bool> taskFunction)
        {
            TaskName = name;
            Frequency = freq;
            Offset = offset;
            TaskFunction = taskFunction;
        }

        /// <summary>
        /// Creates a daily task that will run at the specified hour and minute.
        /// </summary>
        /// <param name="taskName">The task name.</param>
        /// <param name="hourAndMinute">The hour and minute when the task should run (Central Standard Time).</param>
        public static ServerTask CreateDaily(string taskName, Func<bool> taskFunction, TimeSpan hourAndMinute)
        {
            ServerTask thisTask = new ServerTask(taskName, TaskFrequency.Daily, hourAndMinute, taskFunction);
            thisTask.QueueTask();
            return thisTask;
        }

        /// <summary>
        /// Creates a weekly task that will run on the specified weekday, hour, and minute.
        /// </summary>
        /// <param name="taskName">The task name.</param>
        /// <param name="weekday">The day of the week when the task should run.</param>
        /// <param name="hourAndMinute">The hour and minute when the task should run (Central Standard Time).</param>
        public static ServerTask CreateWeekly(string taskName, Func<bool> taskFunction, DayOfWeek weekday, TimeSpan hourAndMinute)
        {
            TimeSpan ts = new TimeSpan((int)weekday, hourAndMinute.Hours, hourAndMinute.Minutes, 0);
            ServerTask thisTask = new ServerTask(taskName, TaskFrequency.Weekly, ts, taskFunction);
            thisTask.QueueTask();
            return thisTask;
        }

        /// <summary>
        /// Creates a monthly task that will run on the specified day of the month, hour, and minute.
        /// </summary>
        /// <param name="taskName">The task name.</param>
        /// <param name="dayOfTheMonth">The day of the month when the task should run.</param>
        /// <param name="hourAndMinute">The hour and minute when the task should run (Central Standard Time).</param>
        public static ServerTask CreateMonthly(string taskName, Func<bool> taskFunction, int dayOfTheMonth, TimeSpan hourAndMinute)
        {
            TimeSpan ts = new TimeSpan(dayOfTheMonth, hourAndMinute.Hours, hourAndMinute.Minutes, 0);
            ServerTask thisTask = new ServerTask(taskName, TaskFrequency.Monthly, ts, taskFunction);
            thisTask.QueueTask();
            return thisTask;
        }

        private enum TaskFrequency
        {
            Daily,
            Weekly,
            Monthly
        }

        private void QueueTask()
        {
            try
            {
                OnCacheRemove = new CacheItemRemovedCallback(RunTask);
                HttpRuntime.Cache.Insert(TaskName, 1, null,
                    getNextTaskTime(), Cache.NoSlidingExpiration,
                    CacheItemPriority.NotRemovable, OnCacheRemove);
            }
            catch { }
        }

        private void RunTask(string taskName, object o, CacheItemRemovedReason r)
        {
            //First, verify that the cache entry has expired. It turns out the server often removes our Cache
            //item even when it hasn't expired (generally about once per hour - there must be a server process
            //that cleans out the cache regularly). If that happens, just re-queue the task again.
            if (r == CacheItemRemovedReason.Expired)
            {
                try
                {
                    Exception taskException = null;
                    bool taskResult = false;

                    Stopwatch taskTimer = Stopwatch.StartNew();
                    try
                    {
                        taskResult = TaskFunction();
                    }
                    catch (Exception e)
                    {
                        taskException = e;
                    }
                    taskTimer.Stop();

                    //Write a log file entry each time this task runs in a monthly log file
                    if (!Directory.Exists(Path.GetDirectoryName(TaskLogPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(TaskLogPath));
                    }
                    DateTime taskTime = Data.DataHelper.ConvertUTCtoCST(DateTime.UtcNow);
                    string logFileName = string.Format("Tasks.{0}.log", taskTime.ToString("MM.yyyy"));
                    using (StreamWriter sw = new StreamWriter(Path.Combine(TaskLogPath, logFileName), true))
                    {
                        if (taskException == null)
                        {
                            string taskResultString = taskResult ? "and reported success" : "but did not report successful completion.";
                            sw.WriteLine("The task \"{0}\" ran at {1} {2}. Task runtime was {3} milliseconds.",
                                taskName, taskTime.ToString(), taskResultString, taskTimer.ElapsedMilliseconds.ToString());
                        }
                        else
                        {
                            sw.WriteLine("Attempted to run the task \"{0}\" at {1}, but the task failed with the following error after {2} milliseconds: {3} [StackTrace: {4}]",
                                taskName, taskTime.ToString(), taskTimer.ElapsedMilliseconds.ToString(), taskException.Message, taskException.StackTrace);
                        }
                    }
                }
                catch { }
            }

            //queue this task to be called again
            QueueTask();
        }

        private DateTime getNextTaskTime()
        {
            if (Frequency == TaskFrequency.Monthly)
            {
                return getNextMonthlyTime();
            }
            else if (Frequency == TaskFrequency.Weekly)
            {
                return getNextWeeklyTime();
            }
            return getNextDailyTime();
        }

        private DateTime getNextDailyTime()
        {
            DateTime now = Data.DataHelper.ConvertUTCtoCST(DateTime.UtcNow);
            DateTime taskTime = new DateTime(now.Year, now.Month, now.Day, Offset.Hours, Offset.Minutes, 0);
            taskTime = taskTime > now ? taskTime : taskTime.AddDays(1);
            taskTime = Data.DataHelper.ConvertCSTtoUTC(taskTime);
            return taskTime;
        }
        private DateTime getNextWeeklyTime()
        {
            DateTime now = Data.DataHelper.ConvertUTCtoCST(DateTime.UtcNow);
            DateTime taskTime = new DateTime(now.Year, now.Month, now.Day, Offset.Hours, Offset.Minutes, 0);
            while ((int)taskTime.DayOfWeek != Offset.Days)
            {
                taskTime = taskTime.AddDays(1);
            }
            taskTime = taskTime > now ? taskTime : taskTime.AddDays(7);
            taskTime = Data.DataHelper.ConvertCSTtoUTC(taskTime);
            return taskTime;
        }
        private DateTime getNextMonthlyTime()
        {
            DateTime now = Data.DataHelper.ConvertUTCtoCST(DateTime.UtcNow);
            DateTime taskTime = new DateTime(now.Year, now.Month, Offset.Days, Offset.Hours, Offset.Minutes, 0);
            if (taskTime.Day < now.Day)
            {
                taskTime = taskTime.AddMonths(1);
            }
            else if (taskTime.Day == now.Day)
            {
                taskTime = taskTime > now ? taskTime : taskTime.AddMonths(1);
            }
            taskTime = Data.DataHelper.ConvertCSTtoUTC(taskTime);
            return taskTime;
        }

        private DateTime ConvertCSTtoUTC(DateTime cstTime)
        {
            try
            {
                TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
                DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(cstTime, cstZone);
                return utcTime;
            }
            catch { }
            return cstTime; //fallback to unconverted time
        }

        private DateTime ConvertUTCtoCST(DateTime utcTime)
        {
            try
            {
                TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
                DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, cstZone);
                return cstTime;
            }
            catch { }
            return utcTime; //fallback to unconverted time
        }
    }
}

任务方法的签名

public static class TaskUpdateUserRoles
{
    public static bool Task()
    {
        //perform task here. Return true if successful.
    }
}