Patterns of Enterprise Application Architecture defines Lazy Load as:

An object that doesn't contain all of the data you need but knows how to get it.

A while back I was trying to figure out how to lazy load objects from a container, so that I didn't need to depend on the objects dependencies needing to be wired up in the correct order. The syntax I was looking for was something like the following....

1 container.AddProxyOf(
2  new ReportPresenterTaskConfiguration(),
3       new ReportPresenterTask(
4  Lazy.Load<IReportDocumentBuilder>(),
5  Lazy.Load<IApplicationSettings>())
6     );

Lazy.Load will return a proxy in place of an actual implementation. This is just a temporary place holder that will forward the calls to the actual implementation. It wont load an instance of the actual type until the first time a call is made to it.

 1 public class when_calling_a_method_with_no_arguments_on_a_lazy_loaded_proxy : lazy_loaded_object_context
 2   {
 3       [Observation]
 4       public void should_forward_the_original_call_to_the_target()
 5       {
 6           target.should_have_been_asked_to(t => t.OneMethod());
 7       }
 8   
 9       protected override void establish_context()
10       {
11           target = dependency<ITargetObject>();
12   
13           test_container
14               .setup_result_for(t => t.find_an_implementation_of<ITargetObject>())
15               .will_return(target)
16               .Repeat.Once();
17       }
18   
19       protected override void because_of()
20       {
21           var result = Lazy.Load<ITargetObject>();
22           result.OneMethod();
23       }
24   
25       private ITargetObject target;
26   }

So when the method "OneMethod" is called on the proxy. It should forward the call to the target, which can be loaded from the container. The implementation depends on Castle DynamicProxy, and looks like the following...

 1 public static class Lazy
 2   {
 3       public static T Load<T>() where T : class
 4       {
 5           return create_proxy_for<T>(create_interceptor_for<T>());
 6       }
 7   
 8       private static LazyLoadedInterceptor<T> create_interceptor_for<T>() where T : class
 9       {
10           Func<T> get_the_implementation = resolve.dependency_for<T>;
11           return new LazyLoadedInterceptor<T>(get_the_implementation.memorize());
12       }
13   
14       private static T create_proxy_for<T>(IInterceptor interceptor)
15       {
16           return new ProxyGenerator().CreateInterfaceProxyWithoutTarget<T>(interceptor);
17       }
18   }
19   
20   internal class LazyLoadedInterceptor<T> : IInterceptor
21   {
22       private readonly Func<T> get_the_implementation;
23 
24       public LazyLoadedInterceptor(Func<T> get_the_implementation)
25       {
26           this.get_the_implementation = get_the_implementation;
27       }
28 
29       public void Intercept(IInvocation invocation)
30       {
31           var method = invocation.GetConcreteMethodInvocationTarget();
32           invocation.ReturnValue = method.Invoke(get_the_implementation(), invocation.Arguments);
33       }
34   }
35   
36   public static class func_extensions
37   {
38       public static Func<T> memorize<T>(this Func<T> item) where T : class
39       {
40           T the_implementation = null;
41           return () => {
42                      if (null == the_implementation) {
43                          the_implementation = item();
44                      }
45                      return the_implementation;
46                  };
47       }
48   }

"resolve" is a static gateway to the underlying IDependencyRegistry. This idea was totally inspired by JP's strongly typed selective proxies. If you haven't already, you should definitely check it out.

Download the source.

comments powered by Disqus