基本構成
nopCommerceの定期タスク機能は、以下の主要コンポーネントで構成されています:
- IScheduleTaskService(タスク情報取得インターフェース)
- ITask(タスク実装インターフェース)
- Task(タスク実行クラス)
- TaskManager(タスク管理クラス)
- TaskThread(スレッド管理クラス)
インターフェース設計
ITaskインターフェースはすべてのタスク処理クラスが実装する必要がある基本インターフェースです:
namespace Nop.Services.Tasks
{
public partial interface ITask
{
void Execute();
}
}
タスク実行処理
Taskクラスでは、データベースに登録された型情報を元にリフレクションを使用してインスタンスを生成します:
private ITask CreateTask()
{
ITask task = null;
if (this.Enabled)
{
var targetType = System.Type.GetType(this._type);
if (targetType != null)
{
task = Activator.CreateInstance(targetType) as ITask;
}
}
return task;
}
具体的なタスク例
メール送信タスクの実装例:
using System;
using Nop.Core.Infrastructure;
using Nop.Services.Logging;
using Nop.Services.Tasks;
namespace Nop.Services.Messages
{
public partial class EmailQueueProcessingTask : ITask
{
public void Execute()
{
var emailQueueService = EngineContext.Current.Resolve<IQueuedEmailService>();
var sender = EngineContext.Current.Resolve<IEmailSender>();
const int maxAttempts = 3;
var emails = emailQueueService.SearchEmails(
sendImmediately: true,
maxTries: maxAttempts,
pageSize: 10000);
foreach (var email in emails)
{
var bccList = ParseEmailAddresses(email.Bcc);
var ccList = ParseEmailAddresses(email.CC);
try
{
sender.SendEmail(
account: email.EmailAccount,
subject: email.Subject,
body: email.Body,
from: email.From,
fromName: email.FromName,
to: email.To,
toName: email.ToName,
bcc: bccList,
cc: ccList);
email.SentOnUtc = DateTime.UtcNow;
}
catch (Exception ex)
{
LogError(ex, email);
}
finally
{
email.SentTries++;
emailQueueService.UpdateQueuedEmail(email);
}
}
}
private string[] ParseEmailAddresses(string addresses)
{
return string.IsNullOrWhiteSpace(addresses)
? null
: addresses.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
}
private void LogError(Exception ex, QueuedEmail email)
{
var logger = EngineContext.Current.Resolve<ILogger>();
logger.Error($"Failed to send email to {email.To}: {ex.Message}", ex);
}
}
}
タスク管理メカニズム
Taskクラスの実行処理:
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 ex)
{
HandleExecutionError(ex);
}
finally
{
UpdateTaskStatus();
this._isRunning = false;
}
}
private void HandleExecutionError(Exception ex)
{
this._enabled = !this.StopOnError;
this._lastEndUtc = DateTime.UtcNow;
var logger = EngineContext.Current.Resolve<ILogger>();
logger.Error($"Error executing '{this._name}' task: {ex.Message}", ex);
}
private void UpdateTaskStatus()
{
try
{
var taskService = EngineContext.Current.Resolve<IScheduleTaskService>();
var taskRecord = taskService.GetTaskByType(this._type);
if (taskRecord != null)
{
taskRecord.LastStartUtc = this.LastStartUtc;
taskRecord.LastEndUtc = this.LastEndUtc;
taskRecord.LastSuccessUtc = this.LastSuccessUtc;
taskService.UpdateTask(taskRecord);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to update task status: {ex}");
}
}
スレッド管理
TaskThreadクラスの主要メソッド:
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;
}