Skip to main content

Lazy loading property with Castle.DynamicProxy2

Today I will solve a problem concerning single responsibility principle and lazy loading properties using Castle DynamicProxy. My example application is a simple bookstore, that can display books from an XML.

public interface IStoreRepository
{
    IList<Book> GetAll();

IList&lt;Author&gt; GetAuthorsForBook(string isbn);

}

domain model

Because the Author object would require a JOIN, we would like to lazy load the values - meaning, when we access the property it will be loaded with values from the datasource.

Castle.DynamicProxy2

Download the dynamic proxy and reference it in your project.

project references

Now create a ProxyBuilder that will be responsible for creating proxies instances for the Book class. It is also possible to create proxies from classes but that is something you may discover by yourself.

public interface IProxyBuilder
{
    T ProxyFromClass<T>() where T : class;
}

public class ProxyBuilder : IProxyBuilder { private readonly IInterceptor[] interceptors; private readonly ProxyGenerator proxyGenerator;

public ProxyBuilder(IInterceptor[] interceptors)
    : this(interceptors, new ProxyGenerator())
{
}

public ProxyBuilder(IInterceptor[] interceptors, ProxyGenerator proxyGenerator)
{
    this.interceptors = interceptors;
    this.proxyGenerator = proxyGenerator;
}

public virtual T ProxyFromClass&lt;T&gt;() where T : class
{
    return proxyGenerator.CreateClassProxy&lt;T&gt;(interceptors);
}

}

If this where a larger application I would use a DI framework and register different instances of IProxyGenerator, for example.

container.Register<IProxyBuilder>("LazyAuthors", new[] { new LazyLoadAuthorsInterceptor() });

Our implementation of the interceptor that will be triggered on every virtual member of the class where it's put.

public class LazyLoadAuthorsInterceptor : IInterceptor
{
    private readonly IStoreRepository repository;
    private const string MethodName = "get_Authors";

public LazyLoadAuthorsInterceptor(IStoreRepository repository)
{
    this.repository = repository;
}

public void Intercept(IInvocation invocation)
{
    if (invocation.Method.Name == MethodName)
    {
        var isbn = ((Book) invocation.InvocationTarget).Isbn;
        invocation.ReturnValue = repository.GetAuthorsForBook(isbn);
    }
}

}

Now we can create a proxy class of book, and when we call the Authors property, the authors will be loaded and returned from IStoreRepository.

var proxyBuilder = new ProxyBuilder(new [] { new LazyLoadAuthorsInterceptor(new StoreRepository()) };
var book = proxyBuilder.ProxyFromClass<Book>();

// Will call IStoreRepository.GetAuthorsForBook var authors = book.Authors;

You may get the code for this here, or you may download it as a zip package.

comments powered by Disqus