Oct 20, 2009

On Becoming a Ninject 2.0 Warrior

I’ve been reading a lot about Inversion of Control (IoC) imageContainers and I finally decided to take the plunge. After a brief survey of Guice, Castle Windsor, Structure Map, Spring, and Ninject, I decided to get my toes wet with Ninject.

The following resources were helpful in getting started:

  1. The dojo documentation that is referenced on the main Ninject site. This will help you even if you are new to the concept of Dependency Injection.
  2. The dimecast screencasts on using Ninject.
  3. Jack Altiere’s blog post on Learning Dependency Injection
  4. September 2010 Update – The project is now hosted on TeamCity even though the Ninject site still mentions GitHub. The GitHub links all appear to be broken and the only releases later than 2.0 that I could find are on the TeamCity site.

My first foray into this went swimmingly so of course I couldn’t stop there. While scanning the Google Ninject newsgroup I noticed the recommendation (as of this writing in October 2009) was to use the Ninject 2.0 beta for new projects rather than the 1.0 release. The original 2.0 beta came out in March 2009 and was announced on the author’s blog. Unfortunately that brief blog post was about the extent of the documentation I could find. In my simple project I found that taking the following steps allowed me to convert from Ninject 1.0 to Ninject 2.0. First you need the new version and it’s a little more work:

  1. Get the latest source code from GitHub
  2. Unzip the archive and double-click the build-release.cmd file you’ll find at the root level. This runs the script to do the build.
  3. The .dll you want will be in the build\net-3.5\release folder.

Then the following steps are required (at a minimum – I only had a very simple project).

  1. Removed old reference to 1.0 .dll and added new reference to Ninject.dll created above.
  2. Changed the Using statements from using Ninject.Core to just using Ninject.
  3. Changed Module classes that derived from StandardModule to derive from NinjectModule instead.
  4. Changed any binding of the form .Using<SingletonBehavior>() to use ".InSingletonScope()"

This all went pretty well except for one thing. The unit test below used to pass in 1.0 and it fails in 2.0. Unfortunately some of my code relied on this behavior.

[TestMethod]
public voidTestSingletonScope()
{
StandardKernel kernel = newStandardKernel();

kernel.Bind<Sword>().ToSelf().InSingletonScope();

kernel.Bind<IWeapon>().To<Sword>().InSingletonScope();

var sword1 = kernel.Get<Sword>();
var sword2 = kernel.Get<IWeapon>();

Assert.AreSame(sword1, sword2);
}

As I learn more about Ninject 2.0 in general and this problem in particular I will update this entry.

imageUpdate 1: Solving the singleton instance mapping

Well that didn’t take long. Turns out support from the Google group is pretty fast. Ian Davis wrote me back with the answer (which was already in the group if I had known enough to do an effective search).

Using the unit test as an example, the key is to bind the IWeapon explicitly to the singleton instance of Sword that is in the kernel container. That is done by binding IWeapon to a delegate that returns the Sword already in the container. Since we are binding to a method we use ToMethod(), which has an overload that will take a delegate. That delegate is defined to take an IContext parameter and return the proper Ninject binding object. Therefore using the lambda syntax we write:

kernel.Bind<IWeapon>.ToMethod( c => c.Kernel.Get<Sword>());

The Ninject 1.0 docs have a page on the Activation Context and talk a little about the IContext. “See, Ninject is pretty smart. When it's resolving a big complex graph of objects, it keeps track of where it is, what's being injected, who's asking for it, etc. All of this information is stored in the context, represented by the IContext interface.”

The proper Unit test looks like this:

[TestMethod]
public void TestSingletonScope()
{
StandardKernel kernel = new StandardKernel();
kernel.Bind<Sword>().ToSelf().InSingletonScope();
kernel.Bind<IWeapon>().ToMethod(c => c.Kernel.Get<Sword>());

var sword1 = kernel.Get<Sword>();
var sword2 = kernel.Get<IWeapon>();
Assert.AreSame(sword1, sword2);
}



See Part Two of Converting From Ninject 1.0 to Ninject 2.0

About Me

My photo
Tod Gentille (@todgentille) is now a Curriculum Director for Pluralsight. He's been programming professionally since well before you were born and was a software consultant for most of his career. He's also a father, husband, drummer, and windsurfer. He wants to be a guitar player but he just hasn't got the chops for it.