从 Autofac Builder 获取所有 AsClosedTypesOf 注册变体
Get all AsClosedTypesOf registration variants from the Autofac Builder
让我们假设这些 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 ...
}
我想自动注册 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 class 以在其构造函数中解析时注册自身:
public SomeCommandHandler(ICommandBus commandBus)
{
// and register here, for example
commandBus.RegisterHandlerbyParam(this);
}
这样我必须向 AsClosedTypesOf 注册结果提供 AutoActivate()。 (一个可能的解决方案,但现在 "handlers" 有两个职责...注册和处理)
这是一个有趣且棘手的问题。泛型肯定会增加这种复杂性,因为非泛型将是一个简单的 IEnumerable<T>
解决方案。
但是... 我想我可以帮上忙。
您将利用...
RegisterAssemblyTypes
中的 OnRegistered
事件,因此您可以查看实际注册的内容。
- 总线的
OnActivating
event,因此您可以注册处理程序。
- 将已注册处理程序类型列表传送到
OnActivating
事件中的闭包。
- 在总线上创建
RegisterHandler
方法的封闭通用版本的一些花哨的反射。
这是一个完整的工作示例,展示了如何做到这一点。请注意,我必须稍微更改 RegisterHandler
的 ICommandBus
接口,因为它不会' 以最初列出的形式为我编译,但您应该能够根据需要进行调整。我运行这个在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());
}
}
让我们假设这些 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 ...
}
我想自动注册 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 class 以在其构造函数中解析时注册自身:
public SomeCommandHandler(ICommandBus commandBus)
{
// and register here, for example
commandBus.RegisterHandlerbyParam(this);
}
这样我必须向 AsClosedTypesOf 注册结果提供 AutoActivate()。 (一个可能的解决方案,但现在 "handlers" 有两个职责...注册和处理)
这是一个有趣且棘手的问题。泛型肯定会增加这种复杂性,因为非泛型将是一个简单的 IEnumerable<T>
解决方案。
但是... 我想我可以帮上忙。
您将利用...
RegisterAssemblyTypes
中的OnRegistered
事件,因此您可以查看实际注册的内容。- 总线的
OnActivating
event,因此您可以注册处理程序。 - 将已注册处理程序类型列表传送到
OnActivating
事件中的闭包。 - 在总线上创建
RegisterHandler
方法的封闭通用版本的一些花哨的反射。
这是一个完整的工作示例,展示了如何做到这一点。请注意,我必须稍微更改 RegisterHandler
的 ICommandBus
接口,因为它不会' 以最初列出的形式为我编译,但您应该能够根据需要进行调整。我运行这个在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());
}
}