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