对比了一下nopcommerce和orchard的计划任务,orchard的复杂的不是一点点,如果想拆下来自己用难度很大,搜索拆了orchard的lucene处理模块,邮件队列拆的discuznt和nopcommerce的结合,计划任务就拆nopcommerce的了,discuznt计划任务设计的没nopcommerce的好。
1.nopcommerce的tasks结构如下:
IScheduleTaskService.cs 接口,这个主要是获取数据库里的任务信息,ScheduleTaskService.cs去实现它就可以了,当然需要在容器里注入一下。
ITask 这个接口比较特别但是很重要,所有的任务处理类都要实现里面唯一的Execute方法。执行计划任务时就需要通过反射来执行这个实现。
namespace Nop.Services.Tasks { /// <summary> /// Interface that should be implemented by each task /// </summary> public partial interface ITask { /// <summary> /// Execute task /// </summary> void Execute(); } }
核心类之一:Task.cs,这个主要是处理任务的执行过程及执行过程类的结果处理。
private ITask CreateTask() { ITask task = null; if (this.Enabled) { var type2 = System.Type.GetType(this._type); if (type2 != null) { task = Activator.CreateInstance(type2) as ITask; } //this._enabled = task != null; } return task; }
通过反射来找到编写的计划任务类。例如下面的发送邮件的任务。
using System; using Nop.Core.Infrastructure; using Nop.Services.Logging; using Nop.Services.Tasks; namespace Nop.Services.Messages { /// <summary> /// Represents a task for sending queued message /// </summary> public partial class QueuedMessagesSendTask : ITask { /// <summary> /// Executes a task /// </summary> public void Execute() { var queuedEmailService = EngineContext.Current.Resolve<IQueuedEmailService>(); var emailSender = EngineContext.Current.Resolve<IEmailSender>(); var maxTries = 3; var queuedEmails = queuedEmailService.SearchEmails(null, null, null, null, true, maxTries, false, 0, 10000); foreach (var queuedEmail in queuedEmails) { var bcc = String.IsNullOrWhiteSpace(queuedEmail.Bcc) ? null : queuedEmail.Bcc.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); var cc = String.IsNullOrWhiteSpace(queuedEmail.CC) ? null : queuedEmail.CC.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); try { emailSender.SendEmail(queuedEmail.EmailAccount, queuedEmail.Subject, queuedEmail.Body, queuedEmail.From, queuedEmail.FromName, queuedEmail.To, queuedEmail.ToName, bcc, cc); queuedEmail.SentOnUtc = DateTime.UtcNow; } catch (Exception exc) { var logger = EngineContext.Current.Resolve<ILogger>(); logger.Error(string.Format("Error sending e-mail. {0}", exc.Message), exc); } finally { queuedEmail.SentTries = queuedEmail.SentTries + 1; queuedEmailService.UpdateQueuedEmail(queuedEmail); } } } } }
执行完任务后需要将数据库里的任务记录状态更改,主要是时间状态变更。
核心执行方法:
/// <summary> /// 执行任务 /// </summary> public void Execute() { this._isRunning = true; try { var task = this.CreateTask(); if (task != null) { this._lastStartUtc = DateTime.UtcNow; task.Execute(); this._lastEndUtc = this._lastSuccessUtc = DateTime.UtcNow; } } catch (Exception exc) { this._enabled = !this.StopOnError; this._lastEndUtc = DateTime.UtcNow; //log error var logger = EngineContext.Current.Resolve<ILogger>(); logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this._name, exc.Message), exc); } try { //find current schedule task var scheduleTaskService = EngineContext.Current.Resolve<IScheduleTaskService>(); var scheduleTask = scheduleTaskService.GetTaskByType(this._type); if (scheduleTask != null) { scheduleTask.LastStartUtc = this.LastStartUtc; scheduleTask.LastEndUtc = this.LastEndUtc; scheduleTask.LastSuccessUtc = this.LastSuccessUtc; scheduleTaskService.UpdateTask(scheduleTask); } } catch (Exception exc) { Debug.WriteLine(string.Format("Error saving schedule task datetimes. Exception: {0}", exc)); } this._isRunning = false; }
任务管理类:TaskManager.cs,主要负责任务的初始化,添加到线程列表,任务的开始和停止。需要在Global里初始化和开始任务,它会根据线程里的定时器自动读取任务列表执行任务。
//开始执行任务 if (databaseInstalled) { TaskManager.Instance.Initialize(); TaskManager.Instance.Start(); }
任务线程管理类:TaskThread.cs,任务线程类,TaskManager将任务都添加到此线程管理类里,此线程管理主要负责判断任务的执行状态,线程执行间隔时间及调用任务执行的主方法Execute,通过Timer定时器实现定时自动运行。
主方法为:
private void Run() { if (_seconds <=0) return; this._startedUtc = DateTime.UtcNow; this._isRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this._isRunning = false; }
从任务列表中读取任务并执行。
以上是简单的分析,目前只是拿来主义,在学习和整理的同时加深一下对开源代码的理解。