Skip to content

Dependency Injection With Ninject

nkohari edited this page Sep 13, 2010 · 1 revision

Rather than spending time doing the “busy work” of creating and connecting your objects, you just tell Ninject which objects depend on which, and it figures out the rest. This is done most easily through the [Inject] attribute. The attribute isn’t actually required, but it’s helpful if you’re just learning how to use Ninject. There are also other more advanced ways to instruct Ninject to inject dependencies, which we’ll talk about later.

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

When Ninject sees the [Inject] attribute on a type’s constructor, it tries to resolve a value for each of the constructor’s arguments. But wait a second… IWeapon is an interface. You can’t create an instance of an interface itself, so how does Ninject know what implementation of IWeapon to inject?

This is accomplished through type bindings. A type binding (or just binding) is a mapping between a service type (generally an interface or abstract class), and an implementation type. Bindings are typically expressed via Ninject’s fluent interface. For example, to arm our Samurai with a Sword, we could declare the following binding:

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

Or, if you prefer, there’s a version without generics, which can be useful for automating:

Bind(typeof(IWeapon)).To(typeof(Sword));

This means that whenever Ninject encounters a dependency on IWeapon, it will resolve an instance of Sword and inject it. This dependency resolution process is a recursive one; that is, if Sword has any dependencies of its own, they will also be resolved before the constructor of Samurai is called. (Also, if the dependencies of Sword have dependencies as well, they will be resolved in turn, and so on.) In this way, Ninject can wire up an entire graph of objects for you, with minimal work on your end. You just need to set up the path, and Ninject will follow it.

This idea of type bindings is common in dependency injectors. However, most of the existing frameworks rely on XML mapping files to set up the bindings between types. Through its fluent interface, Ninject allows you to take advantage of the features of your language (like type-safety) and your IDE (like IntelliSense and code completion).

There are other kinds of bindings too. Some of them are more advanced, and will be covered later. Of note is a self-binding. You can use the same type as both the service and the implementation. For example, the following line binds the Samurai type to itself:

Bind<Samurai>().ToSelf();

Bear in mind that only concrete types can be self-bound; abstract types and interfaces won’t work. Also, if you request an instance of a type that can be self-bound, and there are no bindings defined for the type, Ninject will automatically create an implicit self-binding. It’s up to you whether you want to define your bindings explicitly, or let Ninject figure it out.

You can also have multiple bindings for one service type, controlled by conditions that examine the context in which the instance is being resolved. This is the contextual binding system mentioned earlier, and is the key to unlocking the full power of Ninject. Don’t worry, it’s discussed in excruciating detail later. :-)

Continue reading: Injection Patterns