Castle Dynamic Proxy tutorial part XI: When one interface is not enough

This is part eleven of my tutorial on Castle Dynamic Proxy.

So far in the tutorial we’ve covered most of the basics. However, we were always proxying just one type, be it a class or an interface. There is quite often a need to do more. What if a class implements more than one interface, and we want them all proxied? What if we want the proxy to implement interfaces the target type does not implement. Today we’ll talk about how to do just that, so let’s get straight to it.

If you look into the API you may notice that for every kind of proxy there are overloads that take array of types called additionalInterfacesToProxy.

dptutorial_11_intellisense

This is your gateway into extending your proxy with additional capabilities. However, as simple as it may seem, there are few things you should be aware of when using additional interfaces.

Class which does not implement the interface

Let’s start with simple example of class proxy.

[Fact]
public void ClassProxy_should_implement_additional_interfaces()
{
	object proxy = generator.CreateClassProxy(
		typeof(EnsurePartnerStatusRule),
		new[] {typeof(ISupportsInvalidation)},
		new InvalidationInterceptor());

	Assert.IsAssignableFrom<ISupportsInvalidation>(proxy);
}

The actual implementation of EnsurePartnerStatusRole is not important right now, what’s important is that it does not implement the ISupportsInvalidation interface. The tests passes however, so the resulting proxy does implement it (hooray!). How do you think that interface gets implemented?

If you remember interface proxies without target that would be basically it. You get an empty stub of a method, and you more or less have to use interceptors to fill it with logic. Since there is no target the last interceptor in the pipeline must not call invocation.Proceed() since there’s no actual implementation we could proceed to.

Class which implements the interface

What if the type did implement the interface? Let’s see another test.

[Fact]
public void ClassProxy_for_class_already_implementing_additional_interfaces()
{
	object proxy = generator.CreateClassProxy(
		typeof(ApplyDiscountRule),
		new[] {typeof(ISupportsInvalidation)});
	Assert.IsAssignableFrom<ISupportsInvalidation>(proxy);
	Assert.DoesNotThrow(() => (proxy as ISupportsInvalidation).Invalidate());
}

Notice we didn’t provide any interceptor, so if the proxy does not forward the call to the ApplyDiscountRule’ implementation the second assert will fail. So will the test pass?

The answer is – it will not. We still get a method without target. This is surprising (I am surprised), as this is probably not what most of you would expect. I actually think this is an omission and it is likely going to change so that the test would pass but anyway, it’s good to keep it in mind in order to avoid bugs.

This obviously works precisely the same way for interface proxy without target, but what about interface proxy with target?

Interface proxy with target

Let’s see a test:

[Fact]
public void InterfaceProxy_should_implement_additional_interfaces()
{
	object proxy = generator.CreateInterfaceProxyWithTarget(
		typeof(IClientRule),
		new[] {typeof(ISupportsInvalidation)},
		new ApplyDiscountRule());
	Assert.IsAssignableFrom<ISupportsInvalidation>(proxy);
	Assert.DoesNotThrow(() => (proxy as ISupportsInvalidation).Invalidate());
}

We obviously need the ApplyDiscountRule type to implement IClientRule interface, but what about ISupportInvalidation? Notice that for this test also we didn’t pass any interceptor. So will the second assert succeed?

The answer is… well not that obvious to everyone so pay attention – it depends. Actually the first answer is pretty straightforward – it does not have to implement the additional interface, but the second one depends on whether it does or not.

If it does, then the dynamic proxy will pick it up and use that as target. Calls to members of ISupportsInvalidation will be no different than calls to members of IClientRule. If it does not, we’re back in square A – we have no implementation to forward to so we need to delegate that responsibility to interceptors. This is probably what most people would expect anyway. There’s one thing, which although not really related to Dynamic Proxy could create some confusion.

We pass a target object to the method and it’s the actual type of the object that gets examined for presence of additional interfaces, even if you would pass it around via reference to it’s base type which does not implement the interface, still if the actual type does implement it, it would be used as target.

Interface proxy with target interface

What about the last kind of proxy we have?

In case our target does not implement the additional interface we get the same behavior as in every other case. In case it does however, we surprisingly get this:

dptutorial_11_so

Surprised? Well – don’t be because it’s not a bug – it’s a feature! Let me walk you through:

Remember that in case of interface proxy with target an interceptor can change the target during the invocation. Also since the actual target of the proxy is the main interface, dynamic proxy does not examine the object passed as target for presence of any of the additional interfaces. It would make no sense, since you can swap it out anyway – right?

So when we invoke a method from any of the additional interfaces the only target we have is the proxy itself, and that’s what is passed to the invocation. Then, since no interceptor changed the target, when we invoke the method on target, we invoke it on the proxy itself which starts the vicious circle again.

So you still need an interceptor, to do either one of two things to break the circle:

  • Swap the target of an invocation with some other object that implements it.
  • Not call invocation.Proceed() and handle the invocation for itself.

Notice that if you want to go with option nr 1 you can grab the target object passed to the method creating the proxy (using IProxyTargetAccessor) and after making sure it implements the interface at hand, setting it as the target of the invocation.

Summary

Although on the surface it seems like a very simple feature there is quite a lot to learn about additional interfaces. However they are pretty powerful and well worth the learning effort. Although I didn’t provide any specific scenario for today (sorry, I don’t feel very creative right now) there are plenty places you could use this feature (throwing INotifyPropertyChanged on top of your domain model seems to be one of the most popular and useful).