因为无法保证解决模块的顺序,所以我遇到了一些问题:
我有一个模块注册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
服务可能会在这些项目之后注册。
必须有办法实现这个目标吗?
答案 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)));
}
}
}
请注意,模块分辨率顺序现在与运行时分辨率完全分离。