我目前的大部分实现都是基于这里提供的信息:
Ninject拦截任何具有特定属性的方法?
我使用一个自定义的规划策略类,它查找具有给定属性的所有方法(不是ject interceptor属性),如果符合条件,这些方法将被代理。
用法的一个例子是:
Kernel.Components.Add<IPlanningStrategy, CustomPlanningStrategy<LoggingAttribute, LoggerInterceptor>>();
这将然后寻找任何具有[Logging]
属性的方法,然后将使用日志拦截器。
然而,我目前从动态代理中获得InvalidProxyConstructorArgumentsException,当它试图代理具有相关属性的方法时。现在我记得读到你需要虚方法,但是我不记得看到你必须有一个无参数的构造函数。
所有绑定都是针对接口完成的,AOP拦截器通过属性和上面链接中提到的自定义代理计划类发生。
所以有一种方法来获得动态代理(或linfu版本)代理类有构造函数的依赖关系?(所有依赖项都在内核中,所以不是不能解决)。
查看代理生成代码:https://github.com/ninject/ninject.extensions.interception/blob/master/src/Ninject.Extensions.Interception.DynamicProxy/DynamicProxyProxyFactory.cs
if (targetType.IsInterface)
{
reference.Instance = this.generator.CreateInterfaceProxyWithoutTarget(targetType, additionalInterfaces, InterfaceProxyOptions, wrapper);
}
else
{
object[] parameters = context.Parameters.OfType<ConstructorArgument>()
.Select(parameter => parameter.GetValue(context, null))
.ToArray();
reference.Instance = this.generator.CreateClassProxy(targetType, additionalInterfaces, ProxyOptions, parameters, wrapper);
}
可以看到ninject的动态代理扩展只将ConstructorArgument
s传递给Castle动态代理生成器。
所以,在不改变ninject扩展名或创建自己的扩展名的情况下,您需要将所有依赖项作为构造函数参数传递。您还可以尝试属性/方法注入是否有效(参见https://github.com/ninject/ninject/wiki/Injection-Patterns)。
如果你控制代码,你可以向被代理的类添加接口,然后使用"带有目标的接口代理"。这允许从目标(被代理的类)实例化中解耦代理实例化——>目标可以在不更改ninject (-extensions)的情况下注入依赖项。
:具有以下应该被代理的类:
public interface IBar { }
public class Foo
{
public Foo(IBar bar)
{
}
}
和下面的绑定:
Bind<Foo>().ToSelf().Intercept().With<SomeInterceptor>();
Bind<IBar>().To<Bar>();
然后从对象容器中取出一个Foo
:
IResolutionRoot.Get<Foo>();
行不通。
将所有构造函数参数放在ninject上下文中以使其工作
然而,我们可以改变Foo
的检索使其工作:
var bar = IResolutionRoot.Get<IBar>();
IResolutionRoot.Get<Foo>(new ConstructorArgument("bar", bar);
现在这是次优的,因为ninject没有自动进行依赖项解析。
为代理类添加接口以使其更好地工作
我们可以通过使用"interface proxy with target"来解决这个问题。首先,我们向代理类添加一个接口:public interface IFoo{ }
public class Foo : IFoo
{
public Foo(IBar bar)
{
}
}
然后我们将绑定改为:
Bind<IFoo>().To<Foo>().Intercept().With<SomeInterceptor>();
然后从对象容器中检索Foo
:
IResolutionRoot.Get<Foo>();
。
另一个可能更简单(&丑?)的解决方案根据@Daniel的说法:为代理类型添加两个构造函数:
- 一个没有参数的
protected
构造函数。这个是为DynamicProxy创建代理。 - 一个带有参数的
public
/internal
构造函数,用于实例化被代理的类型。
Ninject将自动选择具有最多可解析参数的构造函数
另一种方法是对具有[Logging]
属性的方法的所有类使用基于约定的绑定。然而,这意味着向方法添加[Logging]
属性将影响对象的绑定,这可能是不希望的。
所以这就是它是如何工作的(验证工作):
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class LoggingAttribute : Attribute
{
}
public interface IClassNotToBeIntercepted
{
void DoSomething();
}
public class ClassNotToBeIntercepted : IClassNotToBeIntercepted
{
public void DoSomething() { }
}
public interface IClassToBeIntercepted
{
void DoNotLogThis();
void LogThis();
void LogThisAsWell();
}
public class ClassToBeIntercepted : IClassToBeIntercepted
{
public void DoNotLogThis() { }
[Logging]
public void LogThis() { }
[Logging]
public void LogThisAsWell() { }
}
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("interceptor before {0}", BuildLogName(invocation));
invocation.Proceed();
Console.WriteLine("interceptor after {0}", BuildLogName(invocation));
}
private static string BuildLogName(IInvocation invocation)
{
return string.Format(
"{0}.{1}",
invocation.Request.Target.GetType().Name,
invocation.Request.Method.Name);
}
}
public class DemoModule : NinjectModule
{
public override void Load()
{
this.Bind(convention => convention
.FromThisAssembly()
.SelectAllClasses()
.Where(ContainsMethodWithLoggingAttribute)
.BindDefaultInterface()
.Configure(x => x
.Intercept()
.With<LoggingInterceptor>()));
this.Bind<IClassNotToBeIntercepted>()
.To<ClassNotToBeIntercepted>();
}
private static bool ContainsMethodWithLoggingAttribute(Type type)
{
return type
.GetMethods()
.Any(method => method.HasAttribute<LoggingAttribute>());
}
}
和一个测试:
[Fact]
public void InterceptorTest()
{
var kernel = new StandardKernel();
kernel.Load<DemoModule>();
kernel.Get<IClassNotToBeIntercepted>()
.DoSomething();
kernel.Get<IClassToBeIntercepted>()
.LogThis();
}
将导致以下控制台输出:
interceptor before ClassToBeIntercepted.LogThis
interceptor after ClassToBeIntercepted.LogThis