使用 ninject 的通用装饰器模式
Generic Decorator pattern using ninject
我试图用装饰器包装我的通用接口,但它根本不起作用,在我看来,从其他问题来看,唯一的方法是为每个装饰器显式地这样做,我的问题是关于是否可以用 ninject.
中的特定装饰器包装所有实现特定接口的类型
代码:
static void BindMediatr(IKernel kernel) {
kernel.Components.Add < IBindingResolver, ContravariantBindingResolver > ();
kernel.Bind(scan => scan.FromAssemblyContaining < IMediator > ()
.SelectAllClasses()
.BindDefaultInterface());
kernel.Bind < SingleInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.Get(t));
kernel.Bind < MultiInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.GetAll(t));
kernel.Bind(
x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFromAny(typeof(IAsyncRequestHandler < , > ))
.BindAllInterfaces());
kernel.Bind(typeof(IAsyncRequestHandler < , > ))
.To(typeof(Decorater < , > ))
.WhenInjectedInto < ApiController > ();
}
public class Decorater < TRequest, TResponse >
: IAsyncRequestHandler < TRequest, TResponse >
where TRequest: IAsyncRequest < TResponse > {
IAsyncRequestHandler < TRequest,
TResponse > _decoratee;
public Decorater(IAsyncRequestHandler < TRequest, TResponse > decoratee) {
_decoratee = decoratee;
}
public Task < TResponse > Handle(TRequest message) {
// do something here
}
}
如果您需要修饰所有实现接口的类中的某个方法的调用,您可以尝试代理所有绑定。
Ninject 有一个名为 - Interception 的扩展,它允许您拦截对您方法的特定调用并通过某种逻辑将其包装。
Here 是一篇关于此的好文章。
希望对你有帮助。
我发现这个扩展方法可以解决问题:
public static class KernelExtensions
{
/// <summary>
/// Binds an open generic type to its implementation and adds all its defined decorators
/// </summary>
/// <param name="kernel">Ninject Container</param>
/// <param name="openGenericType">Open generic Type</param>
/// <param name="assembly">Assembly to scan for the open generic type implementation</param>
/// <param name="decoratorTypes">Types of the decorators. Order matters. Order is from the most outer decorator to the inner decorator</param>
public static void BindManyOpenGenericsWithDecorators(this IKernel kernel, Type openGenericType, Assembly assembly, params Type[] decoratorTypes)
{
var allImplementations = GetAllTypesImplementingOpenGenericType(openGenericType, assembly);
foreach (var type in allImplementations.Where(type => !decoratorTypes.Contains(type)))
{
var genericInterface = type.GetInterfaces().FirstOrDefault(x => openGenericType.IsAssignableFrom(x.GetGenericTypeDefinition()));
// real implementation
var parentType = decoratorTypes.Last();
kernel.Bind(genericInterface).To(type)
.WhenInjectedInto(parentType);
}
for (var i = 0; i <= decoratorTypes.Count() - 1; i++)
{
var decoratorType = decoratorTypes[i];
if (i == 0)
{
// most outer decorator
kernel.Bind(openGenericType).To(decoratorType);
}
else
{
// inner decorators
var parentType = decoratorTypes[i - 1];
kernel.Bind(openGenericType).To(decoratorType)
.WhenInjectedInto(parentType);
}
}
}
private static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
return (from type in assembly.GetTypes()
from interfaceType in type.GetInterfaces()
let baseType = type.BaseType
where
(baseType != null && baseType.IsGenericType &&
openGenericType.IsAssignableFrom(baseType.GetGenericTypeDefinition())) ||
(interfaceType.IsGenericType &&
openGenericType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition()))
select type);
}
}
我试图用装饰器包装我的通用接口,但它根本不起作用,在我看来,从其他问题来看,唯一的方法是为每个装饰器显式地这样做,我的问题是关于是否可以用 ninject.
中的特定装饰器包装所有实现特定接口的类型代码:
static void BindMediatr(IKernel kernel) {
kernel.Components.Add < IBindingResolver, ContravariantBindingResolver > ();
kernel.Bind(scan => scan.FromAssemblyContaining < IMediator > ()
.SelectAllClasses()
.BindDefaultInterface());
kernel.Bind < SingleInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.Get(t));
kernel.Bind < MultiInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.GetAll(t));
kernel.Bind(
x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFromAny(typeof(IAsyncRequestHandler < , > ))
.BindAllInterfaces());
kernel.Bind(typeof(IAsyncRequestHandler < , > ))
.To(typeof(Decorater < , > ))
.WhenInjectedInto < ApiController > ();
}
public class Decorater < TRequest, TResponse >
: IAsyncRequestHandler < TRequest, TResponse >
where TRequest: IAsyncRequest < TResponse > {
IAsyncRequestHandler < TRequest,
TResponse > _decoratee;
public Decorater(IAsyncRequestHandler < TRequest, TResponse > decoratee) {
_decoratee = decoratee;
}
public Task < TResponse > Handle(TRequest message) {
// do something here
}
}
如果您需要修饰所有实现接口的类中的某个方法的调用,您可以尝试代理所有绑定。
Ninject 有一个名为 - Interception 的扩展,它允许您拦截对您方法的特定调用并通过某种逻辑将其包装。
Here 是一篇关于此的好文章。
希望对你有帮助。
我发现这个扩展方法可以解决问题:
public static class KernelExtensions
{
/// <summary>
/// Binds an open generic type to its implementation and adds all its defined decorators
/// </summary>
/// <param name="kernel">Ninject Container</param>
/// <param name="openGenericType">Open generic Type</param>
/// <param name="assembly">Assembly to scan for the open generic type implementation</param>
/// <param name="decoratorTypes">Types of the decorators. Order matters. Order is from the most outer decorator to the inner decorator</param>
public static void BindManyOpenGenericsWithDecorators(this IKernel kernel, Type openGenericType, Assembly assembly, params Type[] decoratorTypes)
{
var allImplementations = GetAllTypesImplementingOpenGenericType(openGenericType, assembly);
foreach (var type in allImplementations.Where(type => !decoratorTypes.Contains(type)))
{
var genericInterface = type.GetInterfaces().FirstOrDefault(x => openGenericType.IsAssignableFrom(x.GetGenericTypeDefinition()));
// real implementation
var parentType = decoratorTypes.Last();
kernel.Bind(genericInterface).To(type)
.WhenInjectedInto(parentType);
}
for (var i = 0; i <= decoratorTypes.Count() - 1; i++)
{
var decoratorType = decoratorTypes[i];
if (i == 0)
{
// most outer decorator
kernel.Bind(openGenericType).To(decoratorType);
}
else
{
// inner decorators
var parentType = decoratorTypes[i - 1];
kernel.Bind(openGenericType).To(decoratorType)
.WhenInjectedInto(parentType);
}
}
}
private static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
return (from type in assembly.GetTypes()
from interfaceType in type.GetInterfaces()
let baseType = type.BaseType
where
(baseType != null && baseType.IsGenericType &&
openGenericType.IsAssignableFrom(baseType.GetGenericTypeDefinition())) ||
(interfaceType.IsGenericType &&
openGenericType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition()))
select type);
}
}