Get all AsClosedTypesOf registration variants from the Autofac Builder

后端 未结 1 1070
滥情空心
滥情空心 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 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 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());
      }
    }
    
        

    0 讨论(0)
    提交回复
    热议问题