對比了一下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; }
通過反射來找到編寫的計划任務類。例如下面的發送郵件的任務。
View Code
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; }
從任務列表中讀取任務並執行。
以上是簡單的分析,目前只是拿來主義,在學習和整理的同時加深一下對開源代碼的理解。
