Autofac等待模块可用

Ste*_*tes 5 autofac

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

我有一个注册到一个模块ScheduleServiceScheduleService是负责在设定的时间间隔等的触发事件

我可以加载不同的IScheduable项目,我这样做使用XML Configuration.我遇到的问题是,IScheduable物品需要IScheduleService准备好才能注册它自己.

所以在我的<autofac><modules>身上

<module type="Namespace.ScheduleServiceModule, Namespace" />
Run Code Online (Sandbox Code Playgroud)

然后我的想法是我可以装入尽可能多的不同ISchedulable物品

<module type="SomeNamespace.ScheudleItem1, SomeNamespace />
<module type="SomeNamespace.ScheudleItem2, SomeNamespace />
<module type="SomeNamespace.ScheudleItem3, SomeNamespace />
<module type="SomeNamespace.ScheudleItem4, SomeNamespace />
Run Code Online (Sandbox Code Playgroud)

目前这是我在那些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);
}
Run Code Online (Sandbox Code Playgroud)

这是每个ScheduleItem中的覆盖

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>()));
 }
Run Code Online (Sandbox Code Playgroud)

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

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

A. *_*esa 0

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

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

许多可能的设计之一涉及让调度服务需要可能的依赖项列表。

在此设计中, an 的职责ISchedule是定义可调度操作的参数,您使用 AutofacAdapter模式将每个调度包装到一个ISyncSchedulable操作中,并且ScheduleService需要 aList<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)));
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,模块解析顺序现在与运行时解析完全解耦。