Castle Dynamic Proxy tutorial part XV: Patterns and Antipatterns

This is part fif­teen of my tuto­r­ial on Cas­tle Dynamic Proxy.

We’ve cov­ered almost all of Dynamic Proxy. If you fol­lowed along through this series, you now know 95% of Dynamic Proxy 2.1 fea­tures that get used 99,9% of the time. Now is the time to wrap up, and with that we’ll review some of the most com­mon pit­falls that you may encounter when devel­op­ing code on top of Dynamic Proxy.

Leak­ing this

Con­sider this sim­ple interface/class pair

public interface IFoo
{
	IFoo Bar();
}

public class Foo : IFoo
{
	public IFoo Bar()
	{
		return this;
	}
}

Now, let’s say we cre­ate a proxy for IFoo with tar­get and use it like this:

var foo = GetFoo(); // returns proxy
var bar = foo.Bar();
bar.Bar();

Can you see the bug here? The sec­ond call is per­formed not on a proxy but on a tar­get object itself! Our proxy is leak­ing its target.

This issue obvi­ously does not affect class prox­ies (since in that case proxy and tar­get are the same object). Why does not Dynamic Proxy han­dle this sce­nario on its own? Because there’s no gen­eral easy way to han­dle this. The exam­ple I showed is the most triv­ial one, but prox­ied object can leak this in a myr­iad of dif­fer­ent ways. It can leak it as a prop­erty of returned object, it can leak it as sender argu­ment of raised event, it can assign this to some global vari­able, it can pass itself to a method on one of its own argu­ments etc. Dynamic Proxy can’t pre­dict any of these, nor should it.

In some of these cases there is often not much you can do about it, and its good to know that prob­lem like this exist, and under­stand its con­se­quences. In other cases though, fix­ing the issue is very sim­ple indeed.

public class LeakingThisInterceptor:IInterceptor
{
	public void Intercept(IInvocation invocation)
	{
		invocation.Proceed();
		if(invocation.ReturnValue == invocation.InvocationTarget)
		{
			invocation.ReturnValue = invocation.Proxy;
		}
	}
}

You add an inter­cep­tor (put it as last one in the inter­cep­tors pipeline), that switches the leak­ing tar­get back to proxy instance. It’s as sim­ple as that. Notice that this inter­cep­tor is tar­geted specif­i­cally at the sce­nario from our exam­ple above (tar­get leak­ing via return value). For each case you will need a ded­i­cated interceptor.

Over­ride equality

One of the most com­mon mis­takes when it comes to Dynamic Proxy is not over­rid­ing Equals/GetHashCode meth­ods on proxy gen­er­a­tion hooks and inter­cep­tor selec­tors, which means you’re giv­ing up caching and that in turn cou­pled with bugs in BCL means per­for­mance hit (plus increased mem­ory consumption).

Solu­tion is very sim­ple, and there’s no excep­tions to this rule – always over­ride Equals/GetHashCode meth­ods on all your classes imple­ment­ing either IProx­y­Gen­er­a­tionHook or IInterceptorSelector.

Make your Proxy Gen­er­a­tion Hooks purely functional

Pure func­tion, is a func­tion that for given set of inputs always returns the same out­put. In case of proxy gen­er­a­tion hook, it means that two equal (as spec­i­fied by over­ri­den Equals/GetHashCode meth­ods) proxy gen­er­a­tion hooks will for given type to proxy return the same val­ues from their meth­ods, and when asked again about the same type will again return the same values/throw the same exceptions.

This is a major assump­tion that Dynamic Proxy makes, and that’s what makes the caching mech­a­nism work. If proxy gen­er­a­tion hook is equal to the one already used to gen­er­ate a proxy type, Dynamic Proxy will assume it would return the same val­ues as the other one, which would result in iden­ti­cal proxy type, so it cuts through the gen­er­a­tion process and returns the exist­ing proxy type.

Make your sup­port­ing classes serializable

If you’re going to be seri­al­iz­ing your prox­ies, you should make all the classes that go with it seri­al­iz­able. That includes proxy gen­er­a­tion hooks, inter­cep­tors and inter­cep­tor selec­tors. Oth­er­wise you will get an excep­tion when try­ing to seri­al­ize your prox­ies. It is not manda­tory, but I find it use­ful. Notice that you will need this also when per­sist­ing your proxy assem­bly to disk.

Use Prox­y­Gen­er­a­tionHooks and Inter­cep­torS­e­lec­tors for fine grained control

Do your interceptor’s meth­ods look like this?

public void Intercept(IInvocation invocation)
{
	if(invocation.TargetType!=typeof(Foo))
	{
		invocation.Proceed();
		return;
	}
	if(invocation.Method.Name!="Bar")
	{
		invocation.Proceed();
		return;
	}
	if(invocation.Method.GetParameters().Length!=3)
	{
		invocation.Proceed();
		return;
	}
	DoSomeActualWork(invocation);
}

If they do this often means you’re doing some­thing wrong. Move the deci­sions to proxy gen­er­a­tion hook and inter­cep­tor selector

  • Do I ever want to inter­cept this method? If the answer is no, use proxy gen­er­a­tion hook to fil­ter it out of meth­ods to proxy.

Notice that due to bug in Dynamic Proxy 2.1, if you choose not to proxy method on inter­face proxy, you will get an excep­tion. Workaround for this is to say you want to inter­cept the method, and then use inter­cep­tor selec­tor to return no inter­cep­tors for the method. This bug is fixed in Dynamic Proxy 2.2

  • If I do want to inter­cept this method, which inter­cep­tors do I want to use? Do I need all of them? Do I need just a sin­gle one? Use inter­cep­tor selec­tor to con­trol this.

On the other hand, remem­ber that as every fea­ture this one is also a dou­ble edged sword. Too lib­eral use of proxy gen­er­a­tion hooks and inter­cep­tor selec­tors may greatly decrease effi­ciency of proxy type caching, which may hurt your per­for­mance. As always think how much con­trol you need and what the impli­ca­tions on caching will be. Some­times sin­gle if on top of your inter­cep­tor is lesser evil than increas­ing num­ber of prox­ies required ten­fold. As always – use the pro­filer in sce­nar­ios that mimic your pro­duc­tion sce­nar­ios as closely as pos­si­ble to check which option is the best for you.

SRP applies to interceptors

SRP stands for Sin­gle Respon­si­bil­ity Prin­ci­ple, which means that a class should do just one thing. Many peo­ple seem to for­get about it when it comes to inter­cep­tors. They cre­ate one mon­strous inter­cep­tor class that tries to do all the things they need from Dynamic Proxy – log­ging, secu­rity check­ing, para­me­ter ver­i­fi­ca­tion, aug­ment­ing tar­get objects with behav­ior and many more.

Remem­ber that Dynamic Proxy lets you have many inter­cep­tors per method call. Use this abil­ity to split behav­ior between inter­cep­tors. You may end up with some gen­eral pur­pose inter­cep­tors for things like log­ging that you use for each inter­cepted method on each class. As long as all it does is log­ging – that’s ok.

You may end up with some inter­cep­tors that are used for meth­ods on just some classes, like classes inher­it­ing from com­mon base class. As long as these inter­cep­tors do just one thing – that’s fine.

You may end up with some inter­cep­tors that exist solely for the pur­pose of inter­cept­ing just a sin­gle method on spe­cific class or inter­face. That also is fine. Use inter­cep­tor selec­tors to match inter­cep­tors to their respec­tive tar­gets, and don’t be afraid to have mul­ti­ple inter­cep­tors per method.

Tech­no­rati Tags: ,
  • James

    Hi Krzysztof, many thanks for the great intro­duc­tion. I'm new to Dynamic Proxy but have used the other Cas­tle frame­works and I can't really see why I would use this instead of an AOP framework.

    What does Dynamic Proxy offer that AOP doesn't?

    Cheers!

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @James

    I think you're mis­un­der­stand­ing terms here. AOP and DP are not mutu­ally exclu­sive. AOP is a tech­nique for tack­ling cer­tain kinds of prob­lems. DP is an imple­men­ta­tion pat­tern. More than that — DP is a way (one of many) to imple­ment AOP in your appli­ca­tion (I'm doing that right now in the project I work on).

    You can read about dif­fer­ent ways to do AOP in this post by Ayende: ayende.com/…/7-Approaches-for-AOP-in-.Net.aspx
    As you can see DP is on the list.

    Where it shines as com­pared to other frame­works, is that its exe­cu­tion is deferred, so you can make deci­sions right on the spot when your pro­gram is already running.

    Hope that answers your questions.