Generate lazy proxies on the fly
July 26, 2012 Leave a comment
Imagine you have a service described by a contract IMyContract and an implementation thereof called MyService. That service is needed at several places throughout your application. The instantiation of MyService is expensive (e.g. takes a long time or consumes a lot of resources). In addition, that service is not needed all of the time (e.g. a message handler that calls this service or another depending on the content of a message).
If you apply the DI pattern you could inject a factory into your service consumers. That factory will only be called when a service instance is really needed. But wait! That is a leaky abstraction! You know that your implementation has a certain behavior and thus you change the way your application uses it. Another implementation of IMyContract might not show that behavior. And so you can’t interchange those implementations at will.
One solution would be to use the interception features of Unity or DynamicProxy to achieve some kind of lazy instantiation. But that is neither pretty nor fast.
Another approach would be to hand-craft (or generate using T4-templates) those proxies. Manual coding means a lot of maintenance effort. Templates would still involve some interaction for regenerating the proxies when the interfaces change.
A solution that comes with the performance of hand-crafted and compiled code and the convenience of auto-generation by the container are proxies that are dynamically generated using Reflection.Emit.
A code generator creates classes that are wrappers around a Lazy<T> field and look something like this:
public class MyContract_LazyInstantiationProxy : IMyContract { private Lazy<IMyContract> instance; public MyContract_LazyInstantiationProxy(Func<IMyContract> factory) { Guard.AssertNotNull(factory, "factory"); this.instance = new Lazy<IMyContract>(factory); } public IMyContract Instance { get { return this.instance.Value; } } public string Foo(IBar bar) { return this.Instance.Foo(bar); } }
And it’s really easy to configure using the provided extension method for IUnityContainer:
var container = new UnityContainer(); container.RegisterLazyProxy( x => { x.Contract = typeof(IFooService); x.ServiceImplementation = typeof(FooService); });
Now you can just inject your service into the consumers and don’t have to care wether or not they are lazily instantiated. No leaky abstraction, no factory overload. Just plain constructor injection. Nice!
Grab the source code for the proxy generation here (project TecX.Unity folder Proxies and the test suite that shows how to use it in TecX.Unity.Proxies.Test).