簡體   English   中英

從Autofac Builder獲取所有AsClosedTypesOf注冊變體

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

讓我們假設這些類/接口:

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接口(只是action委托)。

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()。 (一種可能的解決方案,但是現在“處理者”承擔兩個責任……注冊和處理)

這是一個有趣且棘手的問題。 泛型肯定會增加這種復雜性,因為非泛型將是一個簡單的IEnumerable<T>分辨率。

但是... 我想我可以幫忙。

您將利用...

  • RegisterAssemblyTypesOnRegistered事件,以便您可以查看實際注冊的內容。
  • 總線的OnActivating事件 ,因此您可以注冊處理程序。
  • 用於將已注冊處理程序類型列表攜帶到OnActivating事件中的閉包。
  • 一些花哨的思考,以在總線上創建RegisterHandler方法的封閉通用版本。

這是一個完整的工作示例,展示了如何執行此操作。 注意,我不得不為RegisterHandler更改ICommandBus接口,因為它不會以最初列出的形式為我編譯,但是您應該能夠根據需要進行調整。 我在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