简体   繁体   English

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

[英]Get all AsClosedTypesOf registration variants from the Autofac Builder

Lets assume these classes/interfaces: 让我们假设这些类/接口:

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 ...
}

I want to register all implementations of ICommandHandler<> automatically. 我想自动注册ICommandHandler <>的所有实现。 All variants (Register*) are valid solutions even though I would prefer Action parameter while it is more flexible and has no dependency to Handler interface (just action delegate). 所有变体(Register *)都是有效的解决方案,尽管我更喜欢Action参数,但它更灵活并且不依赖Handler接口(只是action委托)。

Autofac has the capability to register types based upon assembly scanning and register the found implementations of a generic interface, for example: Autofac能够基于程序集扫描来注册类型并注册所找到的通用接口实现,例如:

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

So I have all implementations registered. 因此,我已注册了所有实现。 Now I need to Register them all automatically to TheCommandBus . 现在,我需要将它们全部自动注册到TheCommandBus How to do that? 怎么做?

I can do this manually by adding those lines (eg during OnActivated): 我可以通过添加这些行来手动完成此操作(例如在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)
         });

If I want to use such a type in a lambda expression during registration I have the problem, that I need to identify the concrete type, like for this example on activation process for another component. 如果我想在注册期间在lambda表达式中使用这样的类型,我会遇到问题,我需要确定具体的类型,例如本示例中有关其他组件的激活过程。 But I don't want to list all of them manually... want something like this automatically. 但是我不想手动列出所有这些信息……自动需要这样的信息。

How do I catch all ICommandHandler implementations AND have them automatically registered with the Register* method? 如何捕获所有ICommandHandler实现,并使用Register *方法自动注册它们?

Edit: 编辑:

Another variant is to extend the SomeCommandHandler class to register itself when resolved inside its constructor: 另一个变体是扩展SomeCommandHandler类,以便在其构造函数中解析时进行注册:

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

This way I have to provide AutoActivate() to the AsClosedTypesOf registration result. 这样,我必须向AsClosedTypesOf注册结果提供AutoActivate()。 (a possible solution, but now the "handlers" have two responsibilities... registration and handling) (一种可能的解决方案,但是现在“处理者”承担两个责任……注册和处理)

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. 泛型肯定会增加这种复杂性,因为非泛型将是一个简单的IEnumerable<T>分辨率。

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. RegisterAssemblyTypesOnRegistered事件,以便您可以查看实际注册的内容。
  • The OnActivating event for the bus so you can do the registration of the handlers. 总线的OnActivating事件 ,因此您可以注册处理程序。
  • Closures to carry the list of registered handler types into the OnActivating event. 用于将已注册处理程序类型列表携带到OnActivating事件中的闭包。
  • Some fancy-schmancy reflection to create the closed generic version(s) of the RegisterHandler method on the bus. 一些花哨的思考,以在总线上创建RegisterHandler方法的封闭通用版本。

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. 注意,我不得不为RegisterHandler更改ICommandBus接口,因为它不会以最初列出的形式为我编译,但是您应该能够根据需要进行调整。 I ran this in ScriptCs to verify. 我在ScriptC中运行了此程序以进行验证。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM