[英]Is it possible to remove an existing registration from Autofac container builder?
[英]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>
分辨率。
但是... 我想我可以幫忙。
您將利用...
RegisterAssemblyTypes
的OnRegistered
事件,以便您可以查看實際注冊的內容。 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.