0

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

public interface ICommand
{
}

public class SomeCommand : ICommand
{
}

public interface ICommandHandler<T> where T : ICommand
{
   void Handle(T arg);
}

public class SomeCommandHandler : ICommandHandler<SomeCommand>
{
   void Handle(SomeCommand arg){ /* do something */ }
}

public interface ICommandBus
{
   void RegisterHandler<T>(T t) where T : ICommandHandler<T>;
   void RegisterHandlerByParam<T2>(ICommandHandler<T2> t2) where T2 : ICommand;
   void RegisterHandlerMethod<T3>(Action<T3> 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<TheCommandBus>().As<ICommandBus>().OnActivated(args =>
        {
            // now I need to list all implementations here!!! please, no...
            args.Instance.RegisterHandler<ICommandHandler<SomeCommand>>(args.Context.Resolve<ICommandHandler<SomeCommand>>());

            // does not look better to me than before ...
            args.Instance.RegisterHandlerByParam<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>())

            // uses delegate for, but still need to list all variants
            args.Instance.RegisterHandlerMethod<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>().Handle)
         });

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

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

编辑:

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

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

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

4

1 回答 1

3

这是一个有趣且棘手的问题。泛型肯定会增加这种复杂性,因为非泛型将是一个简单的IEnumerable<T>解决方案。

但是……我想我可以帮忙。

你会利用...

  • 事件OnRegistered中,RegisterAssemblyTypes因此您可以查看实际注册的内容。
  • 总线的OnActivating事件,因此您可以注册处理程序。
  • 将注册的处理程序类型列表携带到OnActivating事件中的闭包。
  • 一些花哨的反射来创建RegisterHandler总线上方法的封闭通用版本。

这是一个完整的工作示例,展示了如何执行此操作。请注意,我不得不稍微更改ICommandBus界面,RegisterHandler因为它不会以最初列出的形式为我编译,但您应该能够根据需要进行调整。我在ScriptCs中运行它来验证。

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());
  }
}
于 2016-06-17T16:07:20.257 回答