2013-09-25 22 views
6

Pracuję nad uaktualnieniem naszego projektu z .Net 2 do .Net4.5, jednocześnie przesyłam tyle referencji, ile Mogę do NuGet i upewnić się, że wersje są aktualne.Używanie Castle.Windsor do zarejestrowania przechwytywacza tylko dla klasy pochodnej, a nie dla klasy bazowej

mam problemu z jedną z prób uruchomienia

klasach Test:

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

Test (nie ma konfiguracji dla tego urządzenia):

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

Dodałem komentarz, aby wskazać, gdzie test się nie powiedzie.

O ile mogę powiedzieć problem powstaje w DerivedClassMethodsInterceptorSelector

Selector:

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

Kiedy to sprawia, że ​​porównanie typów, zmienna typu jest System.RuntimeType ale powinny być Employee (przynajmniej to jest moje zrozumienie).

EDIT: Ten problem został występujących przy użyciu Castle.Windsor i Castle.Core 3.2.1 Po dokonaniu Nuget zainstalować pakiet 3.1.0 kod działa zgodnie z oczekiwaniami.

Jestem skłonny do tego, że to błąd, ale mógłbym też zmienić logikę.

+0

Powieliłem to dokładne zachowanie. –

+0

Jakie serwery proxy są używane? Jeśli jest używany? Pracuję z Windsor 3 lata temu, ale jak pamiętam, większość takich problemów dotyczyła dynamicznych serwerów proxy (windsor + proxy + nhibernate czasami powodują uszkodzenie mózgu). Najlepszym sposobem jest zbadanie System.RuntimeType w debuggerze bardziej bliższym - to przodkowie, atrybuty, a nawet nazwa może dać ci informacje do podjęcia decyzji. –

+0

Jedyne proxy, które są używane, to te, które Windsor tworzy automatycznie podczas rejestracji z przechwytującymi, podczas debugowania jest absolutnie żadnych informacji na temat System.RuntimeType. Po krótkim wyglądzie źródła GetType jest wywoływany na obiekcie docelowym przed wywołaniem selektora przechwytywania iw tym przypadku okazuje się, że obiekt docelowy był już obiektem typu. – Phaeze

Odpowiedz

2

udało mi się odtworzyć ten sam problem z wersją 3.3.3 z tego prostego testu jednostkowego:

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

Naprawiłem go poprzez zmianę kodu źródłowego i edycji metody zamkowych TypeUtil.GetTypeOrNull wyglądać tak:

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

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

    return target.GetType(); 
} 

Oczywiście jest to naiwna poprawka, ponieważ problem występuje gdzie indziej i jest to, że zamiast instancji obiektu przekazanej do tej metody, przekazano jej Type. Jednak sprawdzanie, czy przekazany parametr jest typu Type i jeśli tak, zwrócenie go zamiast wywoływania na nim GetType powoduje, że działa.

+0

Dzięki David, wyśledziłem problem z tym samym blokiem kodu, o którym wspomniałeś, chociaż nie wiedziałem, że problem jest tak generyczny, jak go sprowadziłeś. Zgłosiłem problem do trackera problemów Castle'a: https://github.com/castleproject/Core/issues/74 – Phaeze

+0

Awesome, dzięki za przesłanie go na Github –