こんにちは、Simple Injectorを使用しています。プログラム可能な定期的な頻度でコマンドを呼び出すスケジューラ サービスを実装しようとしています。私の設計上の決定は以下のとおりです。
1) バックグラウンド スレッドを実行し、コンテナーから解決しICommandHandler<SyncExternalDataCommand>.Handle(someParameter)
てコマンド ハンドル メソッドを呼び出すサービスがあります。
RegisterCommandAsBusinessUnitCronJob
各呼び出しでパラメータとして渡された ID を使用して、BusinessUnit ごとに呼び出されます。RegistercommandAsCustomerContactCronJob
顧客レコードをループするように実装することもできます。
このようにバックグラウンド スレッドを使用してスケジューラを設計しても問題ないでしょうか。
2) コンポジション ルート コンテナーとスケジューラーの間の結合を最小限に抑えようとしました。
アクション ヘルパー デリゲートを使用するアプローチは、コードをサービス内ではなくコンポジション ルート セクションに保持するのに役立ちます。別の設計を使用することで、このカップリングをさらに減らすことができますか、それともこのカップリングは許容できますか?
コードは以下のとおりです (まだスレッドセーフにされていません)。上記の設計上の決定に対するコメントと、改善/再設計は大歓迎です。
ありがとう、クリス
コンテナのブートストラップ
void Register(Container container)
{
container.RegisterSingle<IUnitOfWorkFactory>(
new UnitOfWorkFactory(container));
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();
container.RegisterSingle<ILogger, Logger>();
container.RegisterSingle<ICronJobService, CronJobService>();
container.Register<ICommandHandler<SyncExternalDataCommand>,
SyncExternalDataCommandHandler>();
var cronJobService = container.GetInstance<ICronJobService>();
cronJobService.RegisterCommandAsBusinessUnitCronJob("* 1 * * *",
(Int32 id) =>
{
var command = new SyncExternalDataCommand()
{
businessUnitId = id
};
using (container.BeginLifetimeScope())
{
// handler must be resolved INSIDE the scope.
var handler =
container.GetInstance<ICommandHandler<SyncExternalDataCommand>>();
handler.Handle(command);
}
}
);
}
スケジューラーサービス
// to be instantiated as a singleton by IoC container
public class CronJobService : ICronJobService
{
// this dictionary is for tasks that will be called with
// the business unit primary key (Int32).
private cronJobs = new Dictionary<Action<Int32>, string>();
public void RegisterCommandAsBusinessUnitCronJob(
string cronString, Action<Int32> commandFactory)
{
cronJobs.Add(commandFactory, cronString);
}
// this below is called when we are running a task
protected static bool RunCreateAndHandleThread(
object parameter)
{
var jobParameters = (ThreadJobParameters)parameter;
if (!cancellationTokenSource.IsCancellationRequested)
{
// we will need to construct new graph with a
// proxy command
jobParameters.helper(jobParameters.businessUnitId);
}
return true;
}
protected static void SomeBackgroundLoop(
RunHandlePollingLoopParameters parameters)
{
IUnitOfWork unitOfWork =
parameters.unitOfWorkFactory.CreateUnitOfWork();
using (unitOfWork)
{
var businessUnits =
unitOfWork.BusinessUnitRepository.Get();
// loop through cron jobs and business units
foreach (var helperFactory in parameters.cronJobs.Keys)
{
// its time to run this job...
if (isTimeToRunCronjob)
{
var jobParameters = new ThreadJobParameters
{
helper = helperFactory,
businessUnitId = businessUnit.Id
};
Task.Factory.StartNew<bool>(
RunCreateAndHandleThread,
jobParameters,
CronJobService.cancellationTokenSource.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
}
}
}
}