1

嗨,我正在使用Simple Injector,我正在尝试实现一个调度程序服务,该服务以可编程的常规频率调用命令。我的设计决策如下:

1)我有一个运行后台线程并从容器解析ICommandHandler<SyncExternalDataCommand>.Handle(someParameter)并调用命令句柄方法的服务。

RegisterCommandAsBusinessUnitCronJob将为每个 BusinessUnit 调用,并在每次调用时将 ID 作为参数传递。我们还可以实现RegistercommandAsCustomerContactCronJob遍历客户记录。

用后台线程这样设计调度器可以吗?

2)我试图将组合根容器和调度程序之间的耦合保持在最低限度。

使用 Action 助手委托的方法有助于将代码保留在组合根部分中,而不是在服务中。通过使用不同的设计,这种耦合是否可以进一步减少,或者这种耦合是否可以接受?

代码如下(它尚未成为线程安全的),非常欢迎对上面的设计决策和改进/重新设计发表评论。

谢谢,克里斯

容器的引导

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. 这不是问题,只要它是Composition Root的一部分。但是,您CronJobService包含很多逻辑,这意味着您可能想要测试它,但这很难,因为您将它与容器和Task.Factory. 如果可以的话,把它分开。

可以使用 a IUnitOfWorkFactory(例如,看看这个 SO question and answer)。但是由于您已经IUnitOfWork注册了 Per Lifetime Scope,所以我真的看不出 a 有什么IUnitOfWorkFactory帮助。所以不用调用CreateUnitOfWork里面的SomeBackgroundLoop,你可以用 . 包裹这个方法using (container.BeginLifetimeScope())。这样,此方法在生命周期范围的上下文中运行,这意味着任何注入IUnitOfWork(在此线程内)都将是相同的工作单元。

于 2012-06-18T10:11:54.157 回答