从Autofac Builder获取所有AsClosedTypesOf注册变体

让我们假设这些类/接口:

public interface ICommand { } public class SomeCommand : ICommand { } public interface ICommandHandler where T : ICommand { void Handle(T arg); } public class SomeCommandHandler : ICommandHandler { void Handle(SomeCommand arg){ /* do something */ } } public interface ICommandBus { void RegisterHandler(T t) where T : ICommandHandler; void RegisterHandlerByParam(ICommandHandler t2) where T2 : ICommand; void RegisterHandlerMethod(Action action) where T3 : ICommand } public class TheCommandBus : ICommandBus { // implements ICommandBus ... } 

我想自动注册ICommandHandler 的所有实现。 所有变体(Register *)都是有效的解决方案,即使我更喜欢Action参数,它更灵活,并且不依赖于Handler接口(只是动作委托)。

Autofac能够根据程序集扫描注册类型,并注册找到的通用接口实现,例如:

 builder.RegisterAssemblyTypes(Assembly.LoadFrom("MyAssembly.dll")) .AsClosedTypesOf(typeof(ICommandHandler)); 

所以我已经注册了所有实现。 现在我需要将它们全部自动注册到TheCommandBus 怎么做?

我可以通过添加这些行手动完成此操作(例如在OnActivated期间):

 builder.RegisterType().As().OnActivated(args => { // now I need to list all implementations here!!! please, no... args.Instance.RegisterHandler<ICommandHandler>(args.Context.Resolve<ICommandHandler>()); // does not look better to me than before ... args.Instance.RegisterHandlerByParam(args.Context.Resolve<ICommandHandler>()) // uses delegate for, but still need to list all variants args.Instance.RegisterHandlerMethod(args.Context.Resolve<ICommandHandler>().Handle) }); 

如果我想在注册期间在lambda表达式中使用这样的类型我有问题,我需要识别具体类型,就像这个关于另一个组件的激活过程的示例一样。 但我不想手动列出所有这些…自动想要这样的东西。

如何捕获所有ICommandHandler实现并使用Register *方法自动注册它们?

编辑:

另一个变体是扩展SomeCommandHandler类,以便在其构造函数中解析时注册自身:

  public SomeCommandHandler(ICommandBus commandBus) { // and register here, for example commandBus.RegisterHandlerbyParam(this); } 

这样我就必须为AsClosedTypesOf注册结果提供AutoActivate()。 (一种可能的解决方案,但现在“处理程序”有两个职责……注册和处理)

这是一个有趣且棘手的问题。 generics肯定会增加复杂性,因为非generics将是一个简单的IEnumerable分辨率。

但是…… 我想我可以提供帮助。

你会利用……

  • RegisterAssemblyTypesOnRegistered事件,以便您查看实际注册的内容。
  • 总线的OnActivating事件 ,以便您可以注册处理程序。
  • 用于将已注册的处理程序类型列表带入OnActivating事件的闭包。
  • 一些奇特的schmancyreflection,以在总线上创建RegisterHandler方法的封闭通用版本。

这是一个完整的工作示例,展示了如何做到这一点。 注意我必须更改RegisterHandlerICommandBus接口,因为它不会以最初列出的forms为我编译,但您应该能够根据需要进行调整。 我在ScriptCs中运行它来validation。

 using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Autofac; public interface ICommand { } public class CommandOne : ICommand { } public class CommandTwo : ICommand { } public interface ICommandHandler where T : ICommand { void Handle(T arg); } public class CommandOneHandler : ICommandHandler { public void Handle(CommandOne arg) { } } public class CommandTwoHandler : ICommandHandler { public void Handle(CommandTwo arg) { } } public interface ICommandBus { IEnumerable Handlers { get; } void RegisterHandler(THandler handler) where THandler : ICommandHandler where TCommand : ICommand; } public class CommandBus : ICommandBus { private readonly List _handlers = new List(); public IEnumerable Handlers { get { return this._handlers; } } public void RegisterHandler(THandler handler) where THandler : ICommandHandler where TCommand : ICommand { this._handlers.Add(handler); } } var builder = new ContainerBuilder(); // Track the list of registered command types. var registeredHandlerTypes = new List(); builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .AsClosedTypesOf(typeof(ICommandHandler<>)) .OnRegistered(e => registeredHandlerTypes.Add(e.ComponentRegistration.Activator.LimitType)); // Initialize the bus by registering handlers on activating. builder.RegisterType() .As() .OnActivating(e => { foreach(var handlerType in registeredHandlerTypes) { // Due to the generic method, some crazy reflection happens. // First, get ICommandHandler interface. var handlerInterfaceType = handlerType.GetInterface("ICommandHandler`1"); // Grab the  from the ICommandHandler. var commandType = handlerInterfaceType.GetGenericArguments()[0]; // Build the closed generic version of RegisterHandler. var registerMethod = typeof(ICommandBus).GetMethod("RegisterHandler").MakeGenericMethod(commandType, handlerType); // Call the closed generic RegisterHandler to register the handler. registerMethod.Invoke(e.Instance, new object[] { e.Context.Resolve(handlerInterfaceType) }); } }) .SingleInstance(); var container = builder.Build(); using(var scope = container.BeginLifetimeScope()) { var bus = scope.Resolve(); foreach(var t in bus.Handlers) { // List the handler types registered. Console.WriteLine(t.GetType()); } }