Autofac等待模块可用

时间:2015-06-02 09:11:56

标签: autofac

因为无法保证解决模块的顺序,所以我遇到了一些问题:

我有一个模块注册ScheduleService这个ScheduleService负责按设定的时间间隔触发事件等。

我可以使用IScheduable加载不同的XML Configuration项目。我遇到的问题是,IScheduable项要求IScheduleService准备就绪,以便它可以自己注册。

所以在<autofac><modules>我有

<module type="Namespace.ScheduleServiceModule, Namespace" />

然后我的想法是我可以加载多个不同的ISchedulable

<module type="SomeNamespace.ScheudleItem1, SomeNamespace />
<module type="SomeNamespace.ScheudleItem2, SomeNamespace />
<module type="SomeNamespace.ScheudleItem3, SomeNamespace />
<module type="SomeNamespace.ScheudleItem4, SomeNamespace />

目前这是我在这些scheduleitem模块中的方式:

protected override void Load(ContainerBuilder builder)
{
    builder.RegisterCallback(registry =>
    {
        var scheduleService = new TypedService(typeof(IScheduleService)); 
        var registrations = registry.RegistrationsFor(scheduleService);
        if (registrations != null && registrations.Any())
        {
            IComponentRegistration componentRegistration = registrations.First();
            componentRegistration.Activated += (sender, args) =>
            {
                IScheduleService scheduleService = args.Instance as IScheduleService;
                if (scheduleService != null)
                {
                    OnScheduleServiceAvailable(args.Context, scheduleService);
                }
            };
        }
    });
    base.Load(builder);
}

这是每个ScheduleItems

中的覆盖
protected override void OnScheduleServiceAvailable(IComponentContext context,
                                                   IScheduleService scheduleService)
{
    scheduleService.Add(
        new SqlSyncSchedulable(Enabled, IntervalMS, ConnectionString, SqlSelect, 
            context.Resolve<ILoggerService>(), 
            context.Resolve<IPersonService>(), 
            context.Resolve<ILoggingEventService>(), 
            context.Resolve<ITemplateService>(), 
            context.Resolve<ITemplateLoggingEventService>(), 
            context.Resolve<IRuntimeSettingsService>()));
 }

这是非常间歇性的。 ISchedule项应该注册,但问题是Schedule服务可能会在这些项目之后注册。

必须有办法实现这个目标吗?

1 个答案:

答案 0 :(得分:0)

我认为你的问题不在于模块的加载顺序,而在于依赖设计。

您应该以不与时间耦合的方式设计模块和依赖项。

许多可能的设计之一涉及使计划服务需要可能的依赖关系列表。

在此设计中,ISchedule的责任在于定义可调度操作的参数,您使用Autofac Adapter模式将每个调度包装到ISyncSchedulable操作中,并且ScheduleService需要List<ISyncSchedulable>才能在初始化时添加它们。

作为一个例子(按照你的例子,但不是逐字记录:我试图提出一个观点而不是提供一个完整的解决方案):

using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using NUnit.Framework;

namespace Example
{
    public interface ISchedule
    {
        bool Enabled { get; }
        long IntervalMs { get; }
        string ConnectionString { get; }
        string SqlSelect { get; }
    }

    public class Schedule : ISchedule
    {
        public bool Enabled
        {
            get { return true; }
        }

        public long IntervalMs
        {
            get { return 100000; }
        }

        public string ConnectionString
        {
            get { return "localhost;blabla"; }
        }

        public string SqlSelect
        {
            get { return "select 1 as A"; }
        }
    }


    // let's assume SqlSyncSchedulable inherits from a common
    // ISyncSchedulable interface
    public interface ISyncSchedulable
    {
        void RunSchedule(ScheduleService scheduleService);
    }

    public class SqlSyncSchedulable : ISyncSchedulable
    {
        public ISchedule Schedule { get; private set; }
        public OtherService OtherService { get; private set; }

        public SqlSyncSchedulable(ISchedule schedule,
            OtherService otherService
            /*,ILoggerService loggerService
            IPersonService personService, */
        )
        {
            Schedule = schedule;
            OtherService = otherService;
            // read interval and other data from schedule,
            // store service references as usual.
        }

        public void RunSchedule(ScheduleService scheduleService)
        {
            throw new NotImplementedException();
        }
    }

    public class OtherService
    {

    }

    public class ScheduleService
    {
        public ScheduleService(IList<ISyncSchedulable> schedulables, OtherService otherService /*, ... other dependencies */)
        {
            // there is no ADD! Autofac gives you a list of all possible
            // ISyncSchedulable components
            SyncSchedulables = schedulables;
            // ... other dependencies
        }

        public IList<ISyncSchedulable> SyncSchedulables { get; set; }

        // this code is not a proper implementation, nor a scheduler,
        // it's just a placeholder
        public void RunSchedules()
        {
            foreach (var schedule in SyncSchedulables)
            {
                // do your operations, involving ...
                schedule.RunSchedule(this);
            }
        }
    }


    public class TestModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);

            builder.RegisterType<ScheduleService>().AsSelf();
            builder.RegisterType<OtherService>().AsSelf();

            // don't worry about which type should be registered,
            // and register each type inheriting from ISchedule
            // coming from the current assembly
            // You can even use a single registration for all the
            // possible implementations of ISchedule, using techniques
            // explained in http://docs.autofac.org/en/latest/register/scanning.html
            builder.RegisterAssemblyTypes(GetType().Assembly)
                .Where(t => t.GetInterfaces().Contains(typeof(ISchedule)))
                .AsImplementedInterfaces()
                .InstancePerDependency();

            // This registration is a partial, because
            // SqlSyncChedulable requires a single parameter
            // of type ISchedule
            builder.RegisterType<SqlSyncSchedulable>()
                .AsImplementedInterfaces();

            // for each ISchedule class, we register automatically
            // a corresponding ISyncSchedulable, which
            builder.RegisterAdapter<ISchedule, ISyncSchedulable>(RegisterISyncSchedulableForEachISchedule)
                .InstancePerDependency();
        }

        private ISyncSchedulable RegisterISyncSchedulableForEachISchedule(IComponentContext context, ISchedule schedule)
        {
            // the parameter of type ISchedule is the corresponding schedule
            var scheduleParam = new TypedParameter(typeof(ISchedule), schedule);
            // all the other params are resolved automatically by Autofac.
            return context.Resolve<ISyncSchedulable>(scheduleParam);
        }
    }

    [TestFixture]
    public class AutofacTest
    {
        [Test]
        public void TestServiceResolution()
        {
            var builder = new ContainerBuilder();
            builder.RegisterModule(new TestModule());
            var container = builder.Build();

            var service = container.Resolve<ScheduleService>();

            Assert.That(service.SyncSchedulables[0].GetType(), Is.EqualTo(typeof(SqlSyncSchedulable)));
        }

    }
}

请注意,模块分辨率顺序现在与运行时分辨率完全分离。