Если вы действительно хотите использовать ASP.NET, то для выполнения запланированных задач я бы использовал комбинацию кэширования SQL Server, WCF и ASP.NET. Вы можете легко изменить его, чтобы он выполнял более короткие или более длинные интервалы (например, ежечасно или еженедельно). Он будет выполнять только ежедневные задачи в выбранное время.
1. Настройка таблицы SQL для ваших задач
Column Name Data Type
Id int
Name varchar(100)
LastRun datetime
Interval int
RunTime datetime
2. Добавить некоторые данные в таблицу
1
My Daily Email Task
08/15/08 (old date)
300 (5 minutes)
09:00
Интервал в основном сообщает серверу, как часто нужно проверять время. Чем короче время, тем точнее будет время выполнения за счет необходимости чаще проверять базу данных.
3. При использовании ORM создайте сопоставление классов
public class Task
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime? LastRun { get; set; }
public virtual int Interval { get; set; }
public virtual TimeSpan? RunTime { get; set; }
}
При этом используется свободный nhibernate. Entity Framework тоже отлично работает.
4. Создание вызовов службы для получения / сохранения задач
public Task[] GetTasks()
{
var main = new Application.Main();
using (var sessionFactory = main.SessionFactory)
using (var session = sessionFactory.OpenSession())
{
return session.Query<Task>().ToArray();
}
}
public bool SaveTask(Task task)
{
var main = new Application.Main();
using (var sessionFactory = main.SessionFactory)
using (var session = sessionFactory.OpenSession())
using (var trans = session.BeginTransaction())
{
session.SaveOrUpdate(task);
trans.Commit();
return trans.WasCommitted;
}
}
Используется WCF для служебных вызовов. Технически, вам не нужен веб-сервис, но я думаю, что он помогает организации, когда вы храните запросы к базе данных отдельно от сайта ASP.NET.
5. Настройте планирование в своем файле global.asax
protected void Application_Start(object sender, EventArgs e)
{
var service = new MyService.MyServiceClient();
var tasks = service.GetTasks();
foreach (var task in tasks)
{
AddTask(task);
}
service.Close();
}
Вышеприведенная функция в основном захватывает все задачи и помещает их в цикл кэширования.
private void AddTask(PortalService.Task task)
{
var onCacheRemove = new CacheItemRemovedCallback(CacheItemRemoved);
HttpRuntime.Cache.Insert(task.Name, task, null,
DateTime.Now.AddSeconds(task.Interval), Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, onCacheRemove);
}
Приведенная выше функция добавляет каждую задачу в базе данных в кэш времени выполнения ASP.NET, который циклически повторяется на основе интервала, указанного в базе данных.
public void CacheItemRemoved(string k, object taskObj, CacheItemRemovedReason r)
{
var task = (MyService.Task)taskObj;
//Run the task if it is the next day and greater than or equal to the runtime
if (task.LastRun.HasValue && task.LastRun.Value.Day != DateTime.Now.Day && task.RunTime.HasValue && task.RunTime.Value.Hours <= DateTime.Now.Hour && task.RunTime.Value.Minutes <= DateTime.Now.Minute))
{
RunTask(ref task);
}
//Add the task again so that this method gets called again
AddTask(task);
}
Когда наш интервал истекает, вызывается указанная выше функция, которая запускает нашу задачу.
6. Создайте функцию, которая запускает логику ваших различных задач
private void RunTask(ref PortalService.Task task)
{
var service = new MyService.MyServiceClient();
switch (task.Name)
{
case "My Daily Email Task":
service.SendDailyEmail();
break;
case "Another Daily Task":
service.DoAnotherOperation();
break;
case "Yet Another Daily Task":
service.DoYetAnotherOperation();
break;
}
//Update the task to indicate that it ran
task.LastRun = DateTime.Now;
service.SaveTask(task);
service.Close();
}
В целом, это решение работает достойно, учитывая множество улучшений, которые могут быть в него внесены. Переработка пула приложений не должна влиять на это решение, если ваш интервал установлен на достаточно низком уровне с момента сохранения последнего состояния запуска в базе данных.