4

I'm using Simple Injector in my project. For integration Simple Injector with Castle.DynamicProxy I'm using this example.

I have following attributes:

public class MyLogAttribute : Attribute { // some code. }

public class MyTimerAttribute : Attribute { // some code. }

Then these attributes apply to method in my service.

public interface IMyService
{
    void Do();
}

public class MyService : IMyService
{
    [MyLog, MyTimer]
    public void Do() { // some code. }
}

In my interceptors i am trying to get a custom attribute that applied to method, using the following code:

public class MyLogIntercept : Castle.DynamicProxy.IInterceptor
{
    public void Intercept(Castle.DynamicProxy.IInvocation invocation)
    {
          var method = invocation.GetConcreteMethod();

          method = invocation.InvocationTarget.GetType().
                        GetMethod(method.Name);

          var attribute = method.GetCustomAttribute<MyLogAttribute>();

          if(attribute != null)   
          { 
             // some code. 
          }
          else
            invocation.Proceed(); 
    }
 }

public class MyTimerIntercept : Castle.DynamicProxy.IInterceptor
{
    public void Intercept(Castle.DynamicProxy.IInvocation invocation)
    {
          var method = invocation.GetConcreteMethod();

          method = invocation.InvocationTarget.GetType().
                       GetMethod(method.Name);

          var attribute = method.GetCustomAttribute<MyTimerAttribute>();

          if(attribute != null)   
          { 
             // some code. 
          }
          else
            invocation.Proceed();   
     }
 }

These interceptors registered using following code:

container.InterceptWith<MyLogIntercept>(
     type => type == typeof(IMyService));

container.InterceptWith<MyTimerIntercept>(
     type => type == typeof(IMyService));

My problem is that when i am trying in Intercept() method get a custom attribute i get null (attribute == null). How can i get my a custom attributes?

P.S. If registered one intercept (MyTimerIntercept or MyLogIntercept it doesn't metter) for my service, in Intercept() method i can get a custom attribute successfully (attribute != null), but if both interceptor registered i have problem (attribute == null).

P.S. I am using Castle.Core 3.3.3

Steven
  • 166,672
  • 24
  • 332
  • 435
sp7
  • 246
  • 3
  • 12

2 Answers2

2

The outer interceptor decorates the second interceptor, so when you call invocation.InvocationTarget.GetType(), you might not get typeof(MyService), but the type becomes Castle.Proxy.IMyServiceProxy. This type obviously doesn't have the attributes declared, so that's why it returns null.

I honestly don't know how to solve this, but this is one of the many reasons why I prefer having SOLID code and use decorators instead of using interceptors.

With SOLID code, the problem goes away completely, because your services will usually only have one method, which removes the need of marking methods with attributes. When you do that, your decorator or interceptor can just apply to everything of the intercepted interface (because it will only have one method) and you don't have to screw around with attributes like this.

If you start using generic interfaces (such as the ICommandHandler<T> abstraction and IQueryHandler<T> abstraction), you can apply decorators to a wide range of implementations, which removes the need to write interceptors. Decorators remove the need to depend on external libraries such as Castle Dynamic Proxy or even your DI library and this makes your code much cleane4 and more maintainable.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Thank you for good answer, Steven. But I decided to use the `System.Runtime.Remoting.Proxies.RealProxy` instead of the `Castle.Core`. It works well. P.S. I tried to use `Autofac` with `Castle.Core` to solve the same problem. It works well. I hope that problem with `Castle.Core` will be solved in the of the new version `SimpleInjector`. – sp7 Jul 22 '15 at 15:49
  • 1
    @sp7: There is nothing 'to solve' in Simple Injector, because Simple Injector doesn't support interception at all. This is a very deliberate design decision, because we feel that interception is an indication of poorly designed software, where Simple Injector tries to push developers into good practice. Unless we change our philosophy, interception support will never be supported. So don't expect this and if you can, improve your application design instead of depending on interception. – Steven Jul 22 '15 at 16:18
  • 2
    I am a little confused that you consider SOLID being against interceptors. Why should it be poor design to generate a proxy to have aspects running separated from the actual code? (Have a look onto Proxy, Wrapper, ... design patterns) Interface segregation principle tells that many small interfaces are preferred over a few large ones. I don't see any reason to have "just one method". When it fits like with the command pattern, thats fine, it mostly doesnt do when implementing a web api, where it doesn't have any benefit to separate a controller's set of functions to "just one" method. – Holger Leichsenring Jul 19 '17 at 14:38
0

As wrote above Steven, this code:

 var method = invocation.GetConcreteMethod();

 method = invocation.InvocationTarget.GetType().
    GetMethod(method.Name);

Get method on proxy type.

To obtain method on real type, you can use MethodInvocationTarget property:

method = invocation.MethodInvocationTarget;
Stas Boyarincev
  • 3,690
  • 23
  • 23