Skip to content

The Activation Process

nkohari edited this page Sep 13, 2010 · 1 revision

Quite a bit of magic happens inside of Ninject when you call the kernel’s Get() method. Here’s a rough sketch of the procedure:

  1. The binding of the type is resolved.
  2. The binding is used to create an activation plan, which describes the means by which the instance will be created and injected.
  3. An instance of the type is requested from the behavior defined in the activation plan. Depending on the behavior’s implementation, the behavior may simply return an existing instance of the type, in which case the rest of the activation process is not executed.
  4. If the behavior indicates that a new instance of the type is necessary, it is requested from the binding’s provider. The provider resolves any necessary arguments and calls the injection constructor.
  5. All properties are injected according to the activation plan.
  6. All methods are injected according to the activation plan.
  7. All fields are injected according to the activation plan.
  8. If the type implements the IInitializable interface, its Initialize() method is called.
  9. If the type implements the IStartable interface, its Start() method is called.

This entire activation procedure can also be customized by altering the strategy chain used by the Activator kernel component. (More on customizations later.)

Returning to our earlier example, we had a WarriorModule:

class WarriorModule : StandardModule {
  public override Load() {
    Bind<IWeapon>().To<Sword>();
    Bind<Samurai>().ToSelf();
  }
}

And we activated an instance of Samurai like this:

class Program {
  public static void Main() {
    IKernel kernel = new StandardKernel(new WarriorModule());
    Samurai warrior = kernel.Get<Samurai>();
    warrior.Attack("the evildoers");
  }
}

The activation process for the Samurai type is as follows. The number to the left indicates the depth at which the activation is occurring, and some steps have been left out for clarity:

  • (1) The kernel resolves the self-binding for the Samurai type that was defined in the WarriorModule.
  • (1) The kernel asks the TransientBehavior that’s managing Samurai to resolve an instance.
  • (1) The Samurai’s TransientBehavior asks the binding’s provider to create a new instance.
  • (2) The Samurai’s StandardProvider asks the kernel to resolve an instance of IWeapon.
  • (2) The kernel resolves the binding from IWeapon to Sword that was defined in the WarriorModule.
  • (2) The kernel asks the TransientBehavior that’s managing Sword to resolve an instance.
  • (2) The @Sword@’s TransientBehavior asks the binding’s provider to create a new instance.
  • (2) The @Sword@’s StandardProvider calls the parameterless constructor of Sword.
  • (2) The kernel returns the newly-created instance of Sword.
  • (1) The @Samurai@’s StandardProvider calls the injection constructor of Samurai, passing it the instance of Sword as an argument.
  • (1) The kernel returns the newly-created instance of Samurai to the site of the method call in Main().

Whew! After all that, we end up with a Samurai armed with a Sword, and the call to its Attack() method results in the same output as our original example: Chopped the evildoers clean in half. Although this might look like a lot of work just to get the same outcome, keep a few things in mind:

  1. All of this happens in the background, and you rarely have to think about what’s really happening. It Just Works.
  2. It actually adds much less overhead than it seems. Honest. :)
  3. This is an overly simplistic example. The more complex your application becomes, the more the power of Ninject will appear.

Continue reading: How Injection Works