Jac*_*goń 7 c# castle castle-dynamicproxy autofac
我有一个使用Autofac的IoC设置并使用AoP拦截器.
通常,我使用这样注册的接口拦截器:
var builder = new ContainerBuilder();
builder.RegisterType<MyType>()
.As<IMyType>()
.UsingConstructor(new Type[0])
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(MyInterceptor));
Run Code Online (Sandbox Code Playgroud)
它有效.但由于某些原因(在这个最小的例子中不明显),我需要注册一个类并将其注入为self(不是通过接口),所以我尝试:
var builder = new ContainerBuilder();
builder.RegisterType<MyType>()
.As<IMyType>()
.AsSelf()
.UsingConstructor(new Type[0])
.EnableClassInterceptors()
.InterceptedBy(typeof(MyInterceptor));
Run Code Online (Sandbox Code Playgroud)
在此设置中,拦截器永远不会被触发.当我在debug中检查注入的依赖项时,它似乎确实是一个子类代理(应该如此),但它的_interceptors私有属性只包含一个实例Castle.DynamicProxy.StandardInterceptor,这显然不是我配置的.
事实上,如果我删除AsSelf()它仍然没有拦截,这导致我得出一个结论,要么我做错了,或者类拦截根本不起作用......?
UPDATE
MyType实际上是从EF继承的DbContext,我试图拦截SaveChanges(),这是虚拟的.只是为了测试我添加了public virtual void Foo()哪些也没有截获.
UPDATE
我之前忽略了这一点,并简化了跳过一个重要的事实:我UsingConstructor()在注册中指定.现在我凭经验发现UsingConstructor()似乎阻止EnableClassInterceptors()了工作.
我正在使用的完整注册:
builder.RegisterType<FooClass>()
.AsSelf()
.InstancePerRequest()
.EnableClassInterceptors()
.UsingConstructor(new Type[0]) // commenting this out solves the issue
.InterceptedBy(typeof(MyInterceptor));
public class FooClass
{
public virtual void Bar()
{
Debugger.Break();
}
public FooClass() { }
public FooClass(int i) { }
}
Run Code Online (Sandbox Code Playgroud)
拦截器可用于其他注射; 这是复杂的代码,但我把断点放在public void Intercept(IInvocation invocation)方法的开头.
注释掉构造函数的选择使它再次起作用.
我将把奖金奖励给任何可以给我一个解决方法的人,或至少一个很好的解释为什么这不起作用.
UPDATE
关于添加构造函数参数的答案,我调查了这个方面,确实:
Foo.GetType().GetConstructors() // Foo is injected, proxied instance of FooClass
Run Code Online (Sandbox Code Playgroud)
实际上返回3个构造函数:
Void .ctor(Castle.DynamicProxy.IInterceptor[])
Void .ctor(Castle.DynamicProxy.IInterceptor[], Int32)
Void .ctor()
Run Code Online (Sandbox Code Playgroud)
无论我是否添加都会发生这种情况UseConstructor().巧合的是,代码并不适合我; 如果我指定了另一个构造函数(不是无参数),它会.
无论如何,我尝试了以下注册:
builder.RegisterType<MyType>()
.As<IMyType>()
.AsSelf()
.InstancePerRequest()
.UsingConstructor(new[] { typeof(IInterceptor[]) })
.EnableClassInterceptors()
.InterceptedBy(typeof(MyInterceptor));
Run Code Online (Sandbox Code Playgroud)
......而且,惊喜,惊喜!它失败了
MyType类型上不存在匹配的构造函数
进一步挖掘,改变那些流利方法的顺序最终解决了它,完整的工作注册是:
builder.RegisterType<MyType>()
.As<IMyType>()
.AsSelf()
.InstancePerRequest()
.EnableClassInterceptors()
.UsingConstructor(new[] { typeof(IInterceptor[]) })
.InterceptedBy(typeof(MyInterceptor));
Run Code Online (Sandbox Code Playgroud)
摘要
我认为这是一个糟糕的API,它具有似乎流畅的API,但在很大程度上依赖于调用流畅方法的顺序.更糟糕的是,它实际上并没有抛出,只是无声地失败.
我还会认为从用户内部了解代理逻辑来设计糟糕的设计(单独留下文档).理想情况下,UsingConstructor()应该在匹配逻辑中封装拦截器的附加事实.也就是说,我自己设计了API,我知道它很容易要求但很难提供某些功能.
无论如何,案件已经结案,我想拥抱你们所有人.我相信是Jim Bolla给出了第一个精确答案,导致了突破,所以饼干给了他.如我错了请纠正我.
当您使用EnableClassInterceptors(),Autofac时,告诉Castle Dynamic Proxy子类化您的类型.这个新的子类型获取新的构造函数签名,添加类型的参数IInterceptor[].同时,默认情况下,Autofac使用它MostParametersConstructorSelector来选择激活类型时要使用的构造函数.所以通常它会在具有IInterceptor[]参数的构造函数上匹配.
当您调用时UsingConstructor(),注册将更改为使用MatchingSignatureConstructorSelector与您指定的参数类型匹配.(在您的情况下,没有.)这会导致Autofac不使用接受IInterceptor[]参数的构造函数,从而导致您的拦截器不会传递到代理中.
我实际上很惊讶它不会因为没有匹配的构造函数而抛出异常,因为这就是代码看起来应该发生的事情.那部分我还没有解决.
编辑:Jim Bolla 的答案是正确的 - usingEnableClassInterceptors创建了一个子类型MyType,它将更改现有的构造函数以添加IInterceptor[]参数(以及无参数构造函数)。然后,您声明的拦截器InterceptedBy将传递给子类型。
添加UsingConstructor方法时,Autofac 将使用无参数构造函数创建子类型,该构造函数不会注册声明的拦截器。这就是为什么你没有达到预期的行为。可以通过强制 Autofac 使用新签名解析构造函数来使其工作。像这样:
var otherBuilder = new ContainerBuilder();
otherBuilder.RegisterType<MyType>()
.As<IMyType>()
.AsSelf()
.EnableClassInterceptors()
.InterceptedBy(typeof(MyInterceptor))
.UsingConstructor(new Type[1] { typeof(IInterceptor[]) })
;
Run Code Online (Sandbox Code Playgroud)
这将按预期工作。这种行为坦率地让我感到惊讶,我本以为拦截器会在 Autofac/DynamicProxy 的第二步构建后添加。我想知道这是否可以被视为一个错误或至少是一个令人惊讶的行为。上面的解决方案有点味道,因为您必须更改构造函数签名才能容纳代理这一点并不明显。
如果您需要使用具有参数的构造函数,则IInterceptor[]参数始终首先添加到继承的构造函数中,因此例如使用int基于 - 的构造函数,您可以编写:.UsingConstructor(new Type[2] {typeof(IInterceptor[]), typeof(int) })
我认为您声明为虚拟和/或想要拦截的方法可能存在问题:这里有一些测试代码显示针对接口和类调用拦截器:
public interface IMyType { void method(); }
public class MyType: IMyType {
public virtual void method() { Console.WriteLine("method"); }
public virtual void method2() { Console.WriteLine("method2"); }
}
public class MyInterceptor : MethodInterceptor
{
protected override void PreProceed(IInvocation invocation)
{
Console.Write("before - ");
}
}
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyType>()
.As<IMyType>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(MyInterceptor));
builder.RegisterType<MyInterceptor>()
.AsSelf();
var container = builder.Build();
var otherBuilder = new ContainerBuilder();
otherBuilder.RegisterType<MyType>()
.AsSelf()
.As<IMyType>()
.EnableClassInterceptors()
.InterceptedBy(typeof(MyInterceptor));
otherBuilder.RegisterType<MyInterceptor>()
.AsSelf();
var otherContainer = otherBuilder.Build();
container.Resolve<IMyType>().method();
// outputs -> before - method
otherContainer.Resolve<IMyType>().method();
// outputs -> before - method
otherContainer.Resolve<MyType>().method();
// outputs -> before - method
otherContainer.Resolve<MyType>().method2();
// outputs -> before - method2
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,第二次注册在所有情况下都会调用拦截器:
您确定注册正确吗?对于第二种情况(IMyType或MyType),您有何解决办法?
| 归档时间: |
|
| 查看次数: |
2779 次 |
| 最近记录: |