Question

I am working on upgrading our project from .Net 2 to .Net4.5, at the same time I'm pushing as many references as I can to NuGet and making sure the versions are current.

I am having a problem getting one of the tests to run

The Test Classes:

        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();
        }
    }

The test (there is no setup for this fixture):

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));

I added a comment to denote where the test fails.

So far as I can tell the problem is arising in the DerivedClassMethodsInterceptorSelector

Selector:

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

When it makes the comparison of types, the type variable is System.RuntimeType but should be Employee (at least this is my understanding).

EDIT: This problem was occurring using Castle.Windsor and Castle.Core 3.2.1, After making NuGet install the 3.1.0 package the code works as expected.

I am leaning towards this being a bug, but I could also just be a change in the logic.

Was it helpful?

Solution

I was able to reproduce the same issue with version 3.3.3 with this simple unit test:

[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;
        }
    }
}

I fixed it by changing Castle's source code and editing method TypeUtil.GetTypeOrNull to look like this:

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

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

    return target.GetType();
}

Of course this is a naive fix, because the problem is somewhere else and it is that instead of an object instance passed to this method, its Type is passed in. However checking if the passed parameter is of type Type and if so returning it instead of calling GetType on it makes it work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top