Dependancy Injection And Ninject

I’m going to look at another useful item to have in your developer toolbox today: Dependancy Injection.

What Is It And Why Should I Care?

Dependancy Injection is a pretty large topic in itself, but what it boils down to is breaking down tightly coupled classes by using interfaces to provide a level of indirection. There is a good example of this on the Ninject Wiki.

class Samurai {
  readonly Sword _sword;
  public Samurai() {
    _sword = new Sword();
  }
  public void Attack(string target) {
    _sword.Hit(target);
  }
}

If we wanted to break the tight coupling between a Samurai and his sword, to allow the samurai to attack with any weapon, we could do the following:

class Samurai {
  private IWeapon _weapon;
  public Samurai(IWeapon weapon) {
    _weapon = weapon;
  }
  public void Attack(string target) {
    _weapon.Hit(target);
  }
}

This allows us to provide the samurai with any weapon that implements the IWeapon interface, removing the tight coupling between the samurai and his weapon. This is the essence of Dependancy Injection.

My framework of choice is Ninject, so let’s look at how to setup a project to use it.

Setting Up Ninject

There are two things we need to do to setup Ninject, the first is to setup a module that will register all our dependancies:

class ExampleModule : StandardModule
{
    public override void Load()
    {
        Bind<ISamurai>().To<Samurai>();
        Bind<IWeapon>().To<Sword>();
    }
}

Next thing to do is create a kernel; this acts as a repository that will let us request instances of the types we registered.

IKernel kernel = new StandardKernel();

Example Usage

Now that the setup has been completed, we can access the object repository using the kernel:

Samurai s = kernel.Get<ISamurai>();

As the kernel uses generics, we can get a strongly typed result. Now, assuming we used the second class definition of Samurai which has a constructor with an IWeapon argument, we’ll receive an instance of a Samurai with a sword weapon. When creating new object instances, Ninject will check what parameters an object’s constructor requires, create new instances of each and inject them automatically. This is how we ended up with a Samurai with a sword, as our config specified that when an IWeapon object is required, a Sword should be provided.

This can become massively useful in a number of ways. For example. say you want to add logging to a class, simply include an ILogger in the constructor, and start logging. You don’t need to worry about setting up the logger as that’s conveniently handled elsewhere. Need to change the method of logging? Change it in the one place and feel a smug satisfaction that you don’t need to check through all your code for that one place where you dealt with it differently.

Activation Behaviours

“But what if I don’t want a new instance every time?” I hear you ask. Ninject provides some flexibility in how new objects are created through Activation Behaviours.

For example, if we only wanted a single instance of a given class, we could configure it like so:

Bind<ISession>().To<Session>().Using<SingletonBehavior>();

Or say we’re writing a multi-threaded application, and only wanted one instance of a particular class per thread:

Bind<ISession>().To<Session>().Using<OnePerThreadBehavior>();

The other supported behaviours are TransientBehaviour, which is the default and creates a new object every time it is requested; and OnePerRequestBehavior, which will create an object instance once for each web request.

Summary

So hopefully you now have an idea of what Dependancy Injection is, what it can do for you, and how to get a simple application architecture up and running with Ninject.

Ninject provides a lot more flexibility than has been presented here, allowing for much more complex activation scenarios: an entire object graph can be created with a single call to IKernel.Get. When you have an object graph several levels deep, this can really help in ensuring things are created in a consistent manner.

Leave a Reply

Your email address will not be published. Required fields are marked *