1

こんにちは、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);
                }
            }
        }
    }
}
4

1 に答える 1

2

このようにバックグラウンド スレッドを使用してスケジューラを設計しても問題ないでしょうか。

もちろん。すべてがスレッドセーフである限り。DI の観点からは問題はありませんが、もちろん、共有依存関係がスレッドセーフであることを確認する必要があります (すべてのマルチスレッド アプリケーションで行う必要があるため)。ただし、マルチスレッドを使用しなくてもサービスのパフォーマンスと速度が十分である場合は、アプリケーションをシングルスレッドにします。これにより、すべてが非常に簡単になります。

別の設計を使用することで、このカップリングをさらに減らすことができますか、それともこのカップリングは許容できますか?

おそらく。あなたCronJobServiceは に依存していContainerます。これは、コンポジション ルートの一部である限り問題ありません。ただし、あなたCronJobServiceには多くのロジックが含まれているため、おそらくテストする必要がありますが、コンテナとTask.Factory. できればこれを分けてください。

a の使用は問題ありIUnitOfWorkFactoryません (たとえば、この SO の質問と回答を見てください)。しかし、すでにPer Lifetime Scope が登録されているため、a がどのように役立つIUnitOfWorkかはわかりません。の中IUnitOfWorkFactoryで を呼び出す代わりに、このメソッドを でラップするだけです。このように、このメソッドはライフタイム スコープのコンテキスト内で実行されます。つまり、(このスレッド内で) 注入されたものはすべて同じ作業単位になります。CreateUnitOfWorkSomeBackgroundLoopusing (container.BeginLifetimeScope())IUnitOfWork

于 2012-06-18T10:11:54.157 に答える