Resource Usage: IoC Containers And Large Object Graphs


In my about adding request/reply to the app controller, I talked about some resource usage problems that IoC containers can introduce if they are not used properly. Here’s that original text, again:

In a system that makes heavy use of an IoC container for automatic dependency injection, it is very easy for resource usage to get out of hand. For example, if an IoC container is used to instantiate a form and its presenter, and that presenter relies on 3 different interfaces that are implemented by other presenters with each of those presenters relying on an interface that is implemented by another form as well as other interfaces that are implemented by yet more presenters with views… the resource utilization of a system such as this quickly gets out of hand. In this example, there are a minimum of 4 forms that are instantiated and injected into presenters – and that only accounts for the second level of forms in the system. As the system becomes larger and the number of forms and other resource intensive objects being instantiated on start up can quickly get out of hand. This is especially dangerous in a limited resource platform, such as the Compact Framework for Windows Mobile devices – which is the context in which my team is currently using the application controller and these patterns.

I went on to talk about how the request/reply and command pattern implementations in my app controller would help to alleviate this problem by lazy loading the command and request handlers through an IoC container. All of this is still true, of course. I’m only repeating this because I felt that it was worth bringing to the foreground of it’s own post instead of hiding in the background of another subject matter.

In addition to the lazy loaded command and request handlers, though, there are other ways of making an IoC container behave and keep resource usage down to a minimum.

 

Autofac And Binding To A Func

Joskha pointed out in the comments of the request/reply post that IoC containers like Autofac “can auto generate Func dependencies given a registration of T.”  The idea is to register a Func that returns the instance of the object or interface. As a pseudo-code example (not for any real IoC container, just to express the intent of the idea via code):

   1: public interface ISomething { }

   2:  

   3: public class SomeImplementation: ISomething { }

   4:  

   5: //pseudo-code for an IoC container registration

   6: RegisterWithFunc<ISomething>(() => {

   7:     return new SomeImplementation();

   8: });

</div> </div>

This will have the same lazy-load effect that I talked about previously by injecting a proxy class into the ISomething dependency and calling the registered Func from the proxy as soon as it is accessed by anything.  Joshka stated that Autofac can do this. I’m fairly sure Ninject can do this, too. I’m not sure about StructureMap, Unity, Spring.NET or any of the other .NET containers, though. Can someone else confirm the different containers? </p>

For those containers that may not have this capability build in, though, it’s not terribly difficult to set this up manually.

 

Proxying A Single Method

This solution was originally posted in my complaint about C# anonymous types being crippled. Harry M suggested a workaround that uses a delegate in a generic proxy class that would let us sort-of have anonymous types in C#. The suggestion was a good one and I ended up using it not for anonymous types, but as a way to provide lazy-loading proxies to my IoC container’s registration.

Here’s the code that Harry posted in the comment:

   1: public class ActionClickListener: OnClickListener

   2: {

   3:    Action<View> _action;

   4:    

   5:    public ActionClickListener(Action<View> action)

   6:    {

   7:           _action = action

   8:    }

   9:    

  10:    public override void OnClick(View v)

  11:    {

  12:        _action(v);

  13:    }

  14: }

</div> </div>

In this particular code example, the OnClickListener base class would provide an OnClick(Vew v) method definition, which is implemented by calling out to an Action that we passed into the constructor. Mattmc3 then provided a usage example a few comments later: </p>

   1: OnClickListener ocl = new ActionClickListener(

   2:     delegate(View v) {

   3:         // do whatever I want to here

   4:         // Console.WriteLine("delegate");

   5:     }

   6: );

</div> </div>

We can use this code as a proxy class to lazy load our dependency in the same way the Func functionality in Autofac works without actually needing that functionality baked into our IoC container. All we need is the ability to register a specific instance of an object against our interface or base class type. In this case, our pseudo-code registration would look like this: </p>

   1: //pseudo-code for an IoC container registration

   2: RegisterInstance<OnClickListener>(ocl);

</div> </div>

Here we are registering the ocl instance of the ActionClickListener as the type of object to return for the OnClickListener base class. When the OnClickListener is injected into an object that expects it and that object calls the .OnClick method of the OnClickListener base class, our proxy class (ActionClickListener) will call the code that was defined in the constructor’s Action delegate. This, again, allows us to lazy load the the real dependencies at runtime. </p>

 

Proxying A Complete Interface

You can easily combine the Action of the previous example with a full interface proxy using the Func idea from Autofac, as well: </p>

   1: public interface IAnotherInterface

   2: {    

   3:     public void Execute();

   4:     public void SomethingElse();

   5: }

   6:  

   7: public class MyPresenter

   8: {

   9:     private IAnotherInterface _iDoSomethingSpecific;

  10:     public MyPresenter(IAnotherInterface iDoSomethingSpecific)

  11:     {

  12:         _iDoSomethingSpecific = iDoSomethingSpecific;

  13:     }

  14:     

  15:     public void DoWhatever()

  16:     {

  17:         _iDoSomethingSpecific.Execute();

  18:         _iDoSomethingSpecific.SomethingElse();

  19:     }    

  20: }

  21:  

  22: public class TheProxy: IAnotherInterface

  23: {

  24:     Func<IAnotherInterface> _lazyLoad;

  25:     IAnotherInterface _something;

  26:     

  27:     public SpecificCommandProxy(Func<IAnotherInterface> lazyLoad)

  28:     {

  29:         _lazyLoad = lazyLoad

  30:     }

  31:     

  32:     private IAnotherInterface EnsureSomething()

  33:     {

  34:         if (_something == null)

  35:         {

  36:             _something = _lazyLoad();

  37:         }

  38:     }

  39:     

  40:     public void Execute()

  41:     {

  42:         EnsureSomething();

  43:         return _something.Execute();

  44:     }

  45:     

  46:     public void SomethingElse()

  47:     {

  48:         EnsureSomething();

  49:         return _something.SomethingElse();

  50:     }

  51: }

  52:  

  53: public class ActualImplementation: IAnotherInterface

  54: {

  55:     public void Execute()

  56:     {

  57:         //do the real processing, here

  58:     }

  59:     

  60:     public void SomethingElse()

  61:     {

  62:         //more processing of the real stuff goes here

  63:     }

  64: }

  65:  

  66: //pseudo-code to register with a full proxy

  67: Register<IAnotherInterface>(new TheProxy(() => {return new ActualImplementation(); }));

</div> </div>

This example provides a complete implementation of the Func capabilities that Autofac has built into it and allows a full interface to be proxy’d rather than just a single method proxy like the example from Harry M. </p>

 

Other Resource Management Solutions

I’m fairly sure that other IoC containers and general IoC container best practices will include other methods of managing resources. Perhaps your IoC container has an auto-proxy for lazy loading in a manner that is similar to NHibernate. Or StructureMap, for example, has the concept of type interceptors built in, which would easily allow you to build a lazy loading proxy.

What resource management techniques do you use with your IoC container(s)?

Partial Book Review: Beautiful Teams.