我想在预定的时间内发送邮件和短信。我不喜欢Windows服务。 是否有任何选项可以在ASP.Net中的自定义时间内安排任务?
请建议哪种方式最好。
提前感谢。
答案 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.
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.
}
}