使用Castle.Windsor仅为派生类注册拦截器,而不是基类

Jus*_*ica 6 castle-windsor castle castle-dynamicproxy

我正在努力将我们的项目从.Net 2升级到.Net4.5,同时我正在向NuGet推送尽可能多的引用,并确保版本是最新的.

我在运行其中一个测试时遇到问题

测试类:

        public class Person
    {
        public static int PersonBaseMethodHitCount { get; set; }
        public virtual void BaseMethod()
        {
            PersonBaseMethodHitCount = PersonBaseMethodHitCount + 1;
        }
        public static int PersonSomeMethodToBeOverriddenHitCount { get; set; }
        public virtual void SomeMethodToBeOverridden()
        {
            PersonSomeMethodToBeOverriddenHitCount = PersonSomeMethodToBeOverriddenHitCount + 1;
        }
    }

    public class Employee : Person
    {
        public static int EmployeeSomeMethodToBeOverriddenHitCount { get; set; }
        public override void SomeMethodToBeOverridden()
        {
            EmployeeSomeMethodToBeOverriddenHitCount = EmployeeSomeMethodToBeOverriddenHitCount + 1;
        }
        public static int EmployeeCannotInterceptHitCount { get; set; }
        public void CannotIntercept()
        {
            EmployeeCannotInterceptHitCount = EmployeeCannotInterceptHitCount + 1;
        }

        public virtual void MethodWithParameter(
            [SuppressMessage("a", "b"), InheritedAttribute, Noninherited]string foo)
        {
        }
    }

    public class MyInterceptor : IInterceptor
    {
        public static int HitCount { get; set; }
        public void Intercept(IInvocation invocation)
        {
            HitCount = HitCount + 1;
            invocation.Proceed();
        }
    }
Run Code Online (Sandbox Code Playgroud)

测试(此夹具没有设置):

var container = new WindsorContainer();
        container.Register(Component.For<MyInterceptor>().ImplementedBy<MyInterceptor>());
        container.Register(
            Component
            .For<Employee>()
            .ImplementedBy<Employee>()
            .Interceptors(InterceptorReference.ForType<MyInterceptor>())
            .SelectedWith(new DerivedClassMethodsInterceptorSelector()).Anywhere);
        container.Register(Classes.FromAssembly(Assembly.GetExecutingAssembly()).Pick().WithService.FirstInterface());

        var employee = container.Resolve<Employee>();
        Person.PersonBaseMethodHitCount = 0;
        Person.PersonSomeMethodToBeOverriddenHitCount = 0;
        Employee.EmployeeCannotInterceptHitCount = 0;
        Employee.EmployeeSomeMethodToBeOverriddenHitCount = 0;
        MyInterceptor.HitCount = 0;
        employee.BaseMethod();
        Assert.That(Person.PersonBaseMethodHitCount, Is.EqualTo(1));
        // The BaseMethod was not overridden in the derived class so the interceptor should not have been called.
        Assert.That(MyInterceptor.HitCount, Is.EqualTo(0));

        Person.PersonBaseMethodHitCount = 0;
        Person.PersonSomeMethodToBeOverriddenHitCount = 0;
        Employee.EmployeeCannotInterceptHitCount = 0;
        Employee.EmployeeSomeMethodToBeOverriddenHitCount = 0;
        MyInterceptor.HitCount = 0;
        employee.SomeMethodToBeOverridden();
        Assert.That(Person.PersonSomeMethodToBeOverriddenHitCount, Is.EqualTo(0));
        Assert.That(Employee.EmployeeSomeMethodToBeOverriddenHitCount, Is.EqualTo(1));
        Assert.That(MyInterceptor.HitCount, Is.EqualTo(1)); //The test errors out on this line

        Person.PersonBaseMethodHitCount = 0;
        Person.PersonSomeMethodToBeOverriddenHitCount = 0;
        Employee.EmployeeCannotInterceptHitCount = 0;
        Employee.EmployeeSomeMethodToBeOverriddenHitCount = 0;
        MyInterceptor.HitCount = 0;
        employee.CannotIntercept();
        Assert.That(Employee.EmployeeCannotInterceptHitCount, Is.EqualTo(1));
        Assert.That(MyInterceptor.HitCount, Is.EqualTo(0));
Run Code Online (Sandbox Code Playgroud)

我添加了一条注释来表示测试失败的地方.

据我所知,问题出现在DerivedClassMethodsInterceptorSelector中

选择:

public class DerivedClassMethodsInterceptorSelector : IInterceptorSelector
{
    public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
    {
        return method.DeclaringType != type ? new IInterceptor[0] : interceptors;
    }
}
Run Code Online (Sandbox Code Playgroud)

当它进行类型比较时,类型变量是System.RuntimeType但应该是Employee(至少这是我的理解).

编辑: 这个问题是使用Castle.Windsor和Castle.Core 3.2.1,在使NuGet安装3.1.0包后,代码按预期工作.

我倾向于这是一个错误,但我也可以只是改变逻辑.

Dav*_*nek 1

我能够通过这个简单的单元测试在 3.3.3 版本中重现相同的问题:

[TestClass]
public class MyUnitTest
{
    [TestMethod]
    public void BasicCase()
    {

        var ProxyFactory = new ProxyGenerator();
        var aopFilters = new IInterceptor[] {new TracingInterceptor()};
        var ConcreteType = typeof(MyChild);

        var options = new ProxyGenerationOptions { Selector = new AopSelector() };

        var proxy = ProxyFactory.CreateClassProxy(ConcreteType, options, aopFilters) as MyChild;
        proxy.DoIt();

    }
}

public class AopSelector : IInterceptorSelector
{
    public IInterceptor[] SelectInterceptors(Type runtimeType, MethodInfo method, IInterceptor[] interceptors)
    {
        Assert.IsTrue(runtimeType == typeof(MyChild));

        return interceptors;
    }
}

public class MyWay
{
    public virtual void DoIt()
    {
        Thread.Sleep(200);
    }
}
public class MyChild : MyWay
{
    public virtual void DoIt2()
    {
        Thread.Sleep(200);
    }
}

public class TracingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        var isProperty = invocation.Method.Name.StartsWith("get_")
                      || invocation.Method.Name.StartsWith("set_");

        if (isProperty)
        {
            invocation.Proceed();
            return;
        }

        LogMethod(invocation);
    }

    protected virtual void LogMethod(IInvocation invocation)
    {
        var target = (invocation.InvocationTarget ?? invocation.Proxy).GetType().Name;
        var stopwatch = Stopwatch.StartNew();

        try
        {
            stopwatch.Start();
            invocation.Proceed();
        }
        finally
        {
            stopwatch.Stop();
            var result = stopwatch.ElapsedMilliseconds;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我通过更改 Castle 的源代码和编辑方法来修复它,TypeUtil.GetTypeOrNull如下所示:

public static Type GetTypeOrNull(object target)
{
    if (target == null)
    {
        return null;
    }

    var type = target as Type;
    if (type != null)
    {
        return type;
    }

    return target.GetType();
}
Run Code Online (Sandbox Code Playgroud)

当然,这是一个幼稚的修复,因为问题出在其他地方,并且不是传递给此方法的对象实例,而是Type传入了它。但是,检查传递的参数是否是类型Type,如果是,则返回它而不是调用GetType就可以了。