Castle.Windsor:3.1中工厂和拦截器的变化?

时间:2012-09-28 08:25:00

标签: c# castle-windsor castle-dynamicproxy windsor-facilities

以下代码按Castle.Windsor 2.5.3格式传递,但在升级到3.1.0后失败

异常是InvalidProxyConstructorArgumentsException,它指出“无法实例化类的代理:Test。无法找到无参数的构造函数。”

    static void Main(string[] args)
    {
        var container = new WindsorContainer();
        container.Register(Component.For<Interceptor>(),
                           Component.For<Test>().UsingFactoryMethod(() => new Test(""))
                                                .Interceptors<Interceptor>());

        var test = container.Resolve<Test>(); //THROWS IN 3.1.0
    }
}

public class Test
{
    public readonly string S;

    public Test(string s)
    {
        S = s;
    }
}

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
    }
}

在我的实际代码中,Test是一个使用工厂方法构建并注入存储库的MongoDatabase。

在我的实际代码中,我还使用了一个AbstractFacility的继承者来注册拦截器。这样我就不必为每个组件注册拦截器。两种形式的拦截器使用似乎在后续解决方案中以相同的方式工作/失败(在2.5.3 / 3.1.0中)。这里提供的是该设施的缩短版本:

public class Facility : AbstractFacility
{
    protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }

    static void KernelComponentRegistered(string key, IHandler handler)
    {
        if (typeof(IInterceptor).IsAssignableFrom(handler.ComponentModel.Implementation)) return;
        handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
    }
}

我查看了Castle.Windsor源代码和抛出代码,期望在给定的类周围包装代理,这就是为什么它正在寻找无参数构造函数。但是在2.5.3中,我认为代理生成代码永远不会被执行,容器会在我的脑海中解析为非代理版本的Test / MongoDatabase

我猜两个问题: 1)有什么变化? 2)如何在不为工厂方法解析的对象生成代理的情况下保持拦截器注册?或者我想如何使用组件的工厂方法生成代理...

2 个答案:

答案 0 :(得分:2)

在2.5.3中,温莎似乎默默无法应用拦截器。在3.1.0中,当Windsor无法将拦截器应用于您已注册的类型时,会引发异常。 Windsor使用其动态代理库通过生成您要求的实例的代理来支持拦截器(AOP)。因此,两个版本中的问题是,您提供的类不能转换为动态代理,因为它没有no-arg构造函数。我认为3.1.0中的行为更正确,因为如果你期望应用拦截器,那么找出问题是很困难的。

如果要保持2.5.3静默失败的行为,只需添加一个检查,看看在您的设施中注册拦截器之前是否可以代理该类型。这样做可能会更好,但这就是我想出的:

try
{
    ProxyGenerator generator = new ProxyGenerator();
    generator.CreateClassProxy(handler.ComponentModel.Implementation);
    handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForType<MyInterceptor>());
}
catch {}

这在许多方面都是可怕的代码,但它会重现您已经习惯的行为。请注意,当你想要拦截器并且正在努力弄清楚为什么没有被拦截时,它不会让你不顺利。

答案 1 :(得分:0)

斯特凡让我走上正轨。拦截器未被分配给2.5.3中的测试这一事实是一个伪装成bug的错误/特征。 3.1.0行为更正确,并迫使您明确了解拦截器的内容。由于我的实际代码涉及工具,这里是解决问题的解决方案:

public class Facility : AbstractFacility
{
    protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }

    static readonly List<Type> TypesNotToIntercept = new List<Type>
    {
        typeof(IInterceptor),   //Don't intercept Interceptors
        typeof(MulticastDelegate),  //Func<> and the like
        typeof(LateBoundComponent), //Resolved with a factory, such as MongoDatabase
    };

    static void KernelComponentRegistered(string key, IHandler handler)
    {
        if (TypesNotToIntercept.Any(type => type.IsAssignableFrom(implementation));
            return;
        handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
    }
}