从Autofac Builder获取所有AsClosedTypes的注册变体 [英] Get all AsClosedTypesOf registration variants from the Autofac Builder

查看:1313
本文介绍了从Autofac Builder获取所有AsClosedTypes的注册变体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  public interface ICommand 
{
}

公共类SomeCommand:ICommand
{
}

公共接口ICommandHandler< T> T:ICommand
{
void Handle(T arg);
}

公共类SomeCommandHandler:ICommandHandler< SomeCommand>
{
void Handle(SomeCommand arg){/ *做某事* /}
}

公共接口ICommandBus
{
void RegisterHandler< ; T>(T t)其中T:ICommandHandler< T>;
void RegisterHandlerByParam< T2>(ICommandHandler< T2> t2)其中T2:ICommand;
void RegisterHandlerMethod< T3>(Action< T3> action)其中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>()。 OnActivated(args => 
{
//现在我需要在这里列出所有实现!!! please,no ...
args.Instance.RegisterHandler< ICommandHandler< SomeCommand>> (args.Context.Resolve< ICommandHandler< SomeCommand>>());

//对我来说比以前好看多了...
args.Instance.RegisterHandlerByParam< SomeCommand> (args.Context.Resolve< ICommandHandler< SomeCommand>>())

//使用委托,但仍需要列出所有变体
args.Instance.RegisterHandlerMethod< SomeCommand>( args.Context.Resolve< ICommandHandler< SomeCommand>>()处理)。
});

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



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



编辑:
$ b

另一个变体是扩展SomeCommandHandler类在构造函数中解析时注册自己:

pre $ public $ SomeCommandHandler(ICommandBus commandBus)

/ /并在这里注册,例如
commandBus.RegisterHandlerbyParam(this);
}

通过这种方式,我必须为AsClosedTypesOf注册结果提供AutoActivate()。 (可能的解决方案,但现在处理程序有两个职责...注册和处理)

解决方案

这是一个有趣的和棘手的问题。泛型无疑增加了这种复杂性,因为去非泛型将是一个简单的 IEnumerable< T> 分辨率。

但是... 我认为我可以提供帮助。



您会利用...




  • RegisterAssemblyTypes 中的 OnRegistered 事件,所以您可以看看已实际注册。

  • OnActivating event ,以便您可以执行处理程序的注册。

  • 将注册处理程序类型列表携带到 OnActivating 事件。

  • 创建的封闭通用版本的一些花哨思想反射RegisterHandler 方法。



下面是一个完整的实例,展示如何操作。注意我必须更改 ICommandBus 接口为 RegisterHandler 有点,因为它不会在最初列出的表单中为我编译,但您应该能够根据需要进行调整。我在 ScriptCs 上运行了这个功能来验证。

 使用系统; 
使用System.Collections.Generic;
使用System.Linq;
使用System.Reflection;
使用Autofac;

public interface ICommand {}
public class CommandOne:ICommand {}
public class CommandTwo:ICommand {}

public interface ICommandHandler< T> 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>处理程序{get; }
void RegisterHandler< TCommand,THandler>(THandler handler)
THandler:ICommandHandler< TCommand>
其中TCommand:ICommand;
}

公共类CommandBus:ICommandBus
{
private只读列表< object> _handlers = new List< object>();

public IEnumerable< object>处理程序
{
获取
{
返回this._handlers;



public void RegisterHandler< TCommand,THandler>(THandler handler)
THandler:ICommandHandler< TCommand>
其中TCommand:ICommand
{
this._handlers.Add(handler);
}
}

var builder = new ContainerBuilder();

//跟踪注册命令类型的列表。
var registeredHandlerTypes = new List< Type>();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsClosedTypesOf(typeof(ICommandHandler<>))
.OnRegistered(e => registeredHandlerTypes.Add(e.ComponentRegistration.Activator。 LimitType));

//通过在激活时注册处理程序初始化总线。
builder.RegisterType< CommandBus>()
.As< ICommandBus>()
.OnActivating(e => {
foreach(registeredHandlerTypes中的var handlerType)
{
//由于通用的方法,会发生一些疯狂的反射。
//首先,获取ICommandHandler< T>接口
var handlerInterfaceType = handlerType.GetInterface(ICommandHandler`1) ;
//从ICommandHandler< T>获取< T>
var commandType = handlerInterfaceType.GetGenericArguments()[0];
//构建RegisterHandler的封闭泛型版本< TCommand,THandler> ;.
var registerMethod = typeof(ICommandBus).GetMethod(RegisterHandler)。MakeGenericMethod(commandType,handlerType);
//调用封闭泛型RegisterHandler< TCommand,THandler>来注册
registerMethod.Invoke(e.Instance,new object [] {e.Context.Resolve(handlerInterfaceType)});
}
})
.S ingleInstance();

var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
var bus = scope.Resolve< ICommandBus>();
foreach(bus.Handlers中的变量)
{
//列出注册的处理程序类型。
Console.WriteLine(t.GetType());
}
}


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

Autofac has the capability to register types based upon assembly scanning and register the found implementations of a generic interface, for example:

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. How to do that?

I can do this manually by adding those lines (e.g. during 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. 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?

Edit:

Another variant is to extend the SomeCommandHandler class to register itself when resolved inside its constructor:

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

This way I have to provide AutoActivate() to the AsClosedTypesOf registration result. (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.

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

这篇关于从Autofac Builder获取所有AsClosedTypes的注册变体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆