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
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
For those containers that may not have this capability build in, though, it’s not terribly difficult to set this up manually.
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:
</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
</div> </div>
We can use this code as a proxy class to lazy load our dependency in the same way the Func
</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
You can easily combine the Action
</div> </div>
This example provides a complete implementation of the Func
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)?
Proxying A Single Method
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: }
1: OnClickListener ocl = new ActionClickListener(
2: delegate(View v) {
3: // do whatever I want to here
4: // Console.WriteLine("delegate");
5: }
6: );
1: //pseudo-code for an IoC container registration
2: RegisterInstance<OnClickListener>(ocl);
Proxying A Complete Interface
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(); }));
Other Resource Management Solutions