Autofac expects module to appear

Since the order of the allowed modules is not guaranteed, I have some problems with achieving this:

I have a module that registers a ScheduleService , this ScheduleService is responsible for trigger events at set intervals, etc.

I can load into different IScheduable elements that I do using XML Configuration . The problem that I have is IScheduable elements, requiring IScheduleService be ready so that it can register it on its own.

So in my <autofac><modules> I have

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

Then the idea was that I could load so many different ISchedulable elements

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

I am currently doing this in these scheditem modules:

 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); } 

This is an override in each of the 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>())); } 

This is pretty choppy. The ISchedule element must be registered, but the problem is that the Schedule service may be registered after these elements.

Should there be a way to achieve this?

+5
source share
1 answer

I think your problem is not in the order of loading the module, but instead in the design of dependencies.

You must design your modules and your dependencies so that they are not time related.

One of many possible projects suggests that a schedule service requires a list of possible dependencies.

In this responsibilitt of ISchedule 's definition of the parameters of a scheduled operation, you use the Autofac Adapter template to wrap each graph in the ISyncSchedulable operation and the ScheduleService requires a List<ISyncSchedulable> to add them during initialization.

As an example (following your example, but not verbatim: I'm trying to do more than give a complete solution):

 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 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 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))); } } } 

Note that the resolution order of the module is now completely decoupled from run-time resolution.

0
source

All Articles