Get all AsClosedTypesOf registration variants from the Autofac Builder

后端 未结 1 1069
滥情空心
滥情空心 2021-01-28 12:17

Lets assume these classes/interfaces:

public interface ICommand
{
}

public class SomeCommand : ICommand
{
}

public interface ICommandHandler where T :         


        
相关标签:
1条回答
  • 2021-01-28 12:40

    This is an interesting and tricky problem. The generics definitely add to that complexity since going non-generic would be a simple IEnumerable<T> resolution.

    But... I think I can help.

    You'll take advantage of...

    • The OnRegistered event in RegisterAssemblyTypes so you can look at what was actually registered.
    • The OnActivating event for the bus so you can do the registration of the handlers.
    • Closures to carry the list of registered handler types into the OnActivating event.
    • Some fancy-schmancy reflection to create the closed generic version(s) of the RegisterHandler method on the bus.

    Here's a full, working example showing how to do it. Note I had to change the ICommandBus interface for RegisterHandler a bit since it wouldn't compile for me in the originally listed form, but you should be able to adapt as needed. I ran this in ScriptCs to verify.

    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<T> where T : ICommand
    {
      void Handle(T arg);
    }
    
    public class CommandOneHandler : ICommandHandler<CommandOne>
    {
      public void Handle(CommandOne arg) { }
    }
    
    public class CommandTwoHandler : ICommandHandler<CommandTwo>
    {
      public void Handle(CommandTwo arg) { }
    }
    
    public interface ICommandBus
    {
      IEnumerable<object> Handlers { get; }
      void RegisterHandler<TCommand, THandler>(THandler handler)
        where THandler : ICommandHandler<TCommand>
        where TCommand : ICommand;
    }
    
    public class CommandBus : ICommandBus
    {
      private readonly List<object> _handlers = new List<object>();
    
      public IEnumerable<object> Handlers
      {
        get
        {
          return this._handlers;
        }
      }
    
      public void RegisterHandler<TCommand, THandler>(THandler handler)
        where THandler : ICommandHandler<TCommand>
        where TCommand : ICommand
      {
        this._handlers.Add(handler);
      }
    }
    
    var builder = new ContainerBuilder();
    
    // Track the list of registered command types.
    var registeredHandlerTypes = new List<Type>();
    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<CommandBus>()
      .As<ICommandBus>()
      .OnActivating(e => {
        foreach(var handlerType in registeredHandlerTypes)
        {
          // Due to the generic method, some crazy reflection happens.
          // First, get ICommandHandler<T> interface.
          var handlerInterfaceType = handlerType.GetInterface("ICommandHandler`1");
          // Grab the <T> from the ICommandHandler<T>.
          var commandType = handlerInterfaceType.GetGenericArguments()[0];
          // Build the closed generic version of RegisterHandler<TCommand, THandler>.
          var registerMethod = typeof(ICommandBus).GetMethod("RegisterHandler").MakeGenericMethod(commandType, handlerType);
          // Call the closed generic RegisterHandler<TCommand, THandler> 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<ICommandBus>();
      foreach(var t in bus.Handlers)
      {
        // List the handler types registered.
        Console.WriteLine(t.GetType());
      }
    }
    
    0 讨论(0)
提交回复
热议问题