16

I have run into a bit of (what I think is) strange behaviour when using Castle's Dynamic Proxy.

With the following code:

class Program
{
    static void Main(string[] args)
    {
        var c = new InterceptedClass();
        var i = new Interceptor();

        var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

        cp.Method1();
        cp.Method2();

        Console.ReadLine();
    }
}

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine(string.Format("Intercepted call to: " + invocation.Method.Name));

        invocation.Proceed();
    }
}

public class InterceptedClass
{
    public virtual void Method1()
    {
        Console.WriteLine("Called Method 1");
        Method2();
    }

    public virtual void Method2()
    {
        Console.WriteLine("Called Method 2");
    }
}

I was expecting to get the output:

  • Intercepted call to: Method1
  • Called Method 1
  • Intercepted call to: Method2
  • Called Method 2
  • Intercepted call to: Method2
  • Called Method 2

However what I got was:

  • Intercepted call to: Method1
  • Called Method 1
  • Called Method 2
  • Intercepted call to: Method2
  • Called Method 2

As far as I can tell then the dynamic proxy is only able to proxy method calls if the call comes from outside the class itself as Method2 was intercepted when called from Program but not from within InterceptedClass.

I can kind of understand that when making calls from within the proxied class it would no longer go through the proxy, but just wanted to check that this was expected and if it is then see if there is there anyway to get all calls intercepted regardless of where they're called from?

Thanks

1 Answers1

19

EDIT: tl;dr - I've just tried creating the proxy in a different way, as described below, and it produces the output you were after. I just had to change this:

var c = new InterceptedClass();
var i = new Interceptor();

var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

To this:

var i = new Interceptor();
var cp = new ProxyGenerator().CreateClassProxy<InterceptedClass>(i);

As I understand it, the proxy generator is effectively creating a wrapper object. They're two separate objects - one is just a wrapper around the other, with interception etc in the wrapper layer.

It's hard to see how it could change what the instance of InterceptedClass did with its own method calls:

  • DynamicProxy can't change the type of an existing object; once an object is created, its type is fixed
  • DynamicProxy can't change how existing calls to an existing object are bound

If you want Method1 to call Method2 via the wrapper using the current proxy creation code, you'll need to tell the existing object about the wrapper, either as a field within it or as a method parameter.

Alternatively, there may be a different way of creating the proxy to start with - one where the proxy is in some sense the target object. I suspect you may want to look at CreateClassProxy rather than CreateClassProxyWithTarget - I suspect it's the fact that you're supplying the target object which is causing you problems.

Whether the behaviour you're seeing is "expected" or not obviously depends on your expectations - but it's certainly what I would expect, without knowing anything about Castle Dynamic Proxy :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yep, I kind of figured as much. So in other terms calls to "cp" can be intercepted and calls to "c" cannot and that includes calls to "c" from within "c" - which is in no way surprising. As an aside: I should probably think about using slightly more descriptive variable names when asking questions, eh? – George Goodchild Jul 09 '11 at 09:41
  • Thanks for the update - the reason for using CreateClassProxyWithTarget is that in the actual problem I am trying to solve the target object is created by an IoC container which I then want to wrap with the proxy. But that particular issue is outside the remit of this question. Thanks for your help. – George Goodchild Jul 09 '11 at 09:55
  • @George: Is there any way you could persuade the IoC container to use Castle Dynamic Proxy itself? As you say, it's slightly separate from this question, but it may be feasible... – Jon Skeet Jul 09 '11 at 09:56