As seems to be the case on this blog, I’m about to confess to something foolish that I’ve done so as to better inform you dear reader!
There are many IoC containers out there in the .Net-o-sphere that are quite excellent. Personally, I use Ninject because I love its simple syntax and its powerful contextual binding. When I first came across Ninject some time ago (shortly after 1.0 was released) I read everything I could about it and IoC in general and then said to myself “IoC … whatever! I don’t need that crap! … way too complex”. A startling lack of vision on my part. Then I started getting deep into my program’s re-design from C to C#. We have a Settings object that contains the settings the user has entered from the command-line and/or a parameter file. Lots of objects need the Settings but sometimes these objects are fairly deep in the object graph and to get the settings I used the Singleton instance of it. As you develop in a more object-oriented way many other circumstances like this turn up.
As I started unit testing I discovered that Ninject was born and raised for just this sort of activity: injecting objects deep into the object graph when you want them and it gets rid of the ugly Singleton instances that are such a pain when you are unit testing. It was like I was born-again. Praise the Ninject! I used Ninject to create everything … and I mean everything! I was passing parameters to the kernel to help resolve bindings, using providers where I thought I needed more complicated logic, you name it. If I could have used Ninject to inject my lunch into my bag every day I would have.
Then it came time to performance test my application. When I ran the performance tests they were incredibly slow … like I could have done the calculations without the computer slower. I did some research on the net and found that there was a performance bug in Ninject 1.0 that was resolved in 1.5 and 2.0 but when I tried replacing 1.0 with 1.5 or 2.0 the performance was barely affected and still very slow.
I was perplexed so of course I went to the Ninject user group and posted a question. The answers sort of slapped me in the face a bit. As one of my friends at work says: I was trying to be holier than the pope! Basically IoC containers are intended to be used to resolve long-lived instances. This is, in fact, why some containers set the default lifetime of objects to singleton rather than transient. To resolve large numbers of transient objects you should use the factory pattern, make the IoC Container resolve the factory as a singleton and inject any dependencies that the transient objects need into the factory. I don’t have many types of transient objects that need to be created, only a few classes, so I created factories for them and my performance problems melted like an arctic ice pack.
So how could I have avoided this problem? I should have been more diligent about asking questions on best practices when I first started using Ninject (or really any other piece of technology). Most worthwhile open source projects have newsgroups or forums that are active and have some informative users who are willing to lend a hand. In my own defense though, while doing research on IoC (and I read quite a few articles on this) never once did I come across best practices for using the tools, it was mostly the nuts and bolts of how to use a tool that documentation focuses on. This particular problem never seemed to be discussed, though if I’m wrong maybe someone will point me to where it is discussed. That’s the main reason I wrote this article though, to help anyone else who may have been in the same situation as me.