Dependency Injection Using Ninject

Ninject

"Ninject is a lightweight dependency injection framework for .NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces, and then glue them back together in a flexible manner. By using Ninject to support your software's architecture, your code will become easier to write, reuse, test, and modify."

- www.ninject.org

Important features of Ninject

  1. Provide fluent interface
  2. Light-weight (122 KB, version-3.0.015)
  3. Faster because of lightweight code generation
  4. Extensible (provide extensions for MVC, WCF and more)

Modules and Kernels

Modules

The Modules are the components that are used to register types. Modules act as independent segments of an application that takes care of binding all the interfaces with implementations of that segment. All the modules should implement the interface INinjectModule. Ninject comes with a built-in abstract class NinjectModule that implements this interface. This abstract class contains a single abstract method Load where we bind the interfaces with concrete types.

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

Kernel

In Ninject the Kernel is the component that controls everything. We can directly bind the interfaces with implementations into Kernel or we can pass them wrapped as modules. Whenever we need any implementation of the mapped interface we can get it right away from the Kernel. Ninject itself comes with a built-in Kernel called StandardKernel.

// add bindings directly into the kernel
var kernel = new StandardKernel();
kernel.Bind<IWeapon>().To<Sword>();

or

// supply bindings as a module
var kernel = new StandardKernel(new WarriorModule);
// get an IWeapon implementation
var weapon = kernel.Get<IWeapon>();

Before trying the sample you can download the Ninject assemblies and extensions from here.

Lets try a sample!

In this sample console application we have a WeatherStation class that randomly displays weather condition through an IDisplay implementation. The dependencies of the WeatherStation are injected through the constructor.

interface IWeatherStation
{
	void DisplayReport();
}

class WeatherStation : IWeatherStation
{
	private readonly IDisplay _display;
	private readonly WeatherCondition[] _weatherConditions;
	private readonly Random _random;

	public WeatherStation(IDisplay display, WeatherCondition[] weatherConditions)
	{
		_display = display;
		_weatherConditions = weatherConditions;
		_random = new Random();
	}

	public void DisplayReport()
	{
		var weatherCondition = _weatherConditions[_random.Next(0, _weatherConditions.Length)];
		_display.SetColor(weatherCondition.Color);
		_display.Write(String.Format("  {0} @ {1}", weatherCondition.Name, DateTime.Now.ToLongTimeString()));
		_display.ResetColor();
	}
}

The IDisplay interface has methods for both to display text and to set foreground color for the text. The ConsoleDisplay class implements IDisplay interface which writes the text into the console.

interface IDisplay
{
	void Write(string message);
	void SetColor(string color);
	void ResetColor();
}

class ConsoleDisplay : IDisplay
{
	public void Write(string message)
	{
		Console.WriteLine(message);
	}

	public void SetColor(string color)
	{
		Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), color);
	}

	public void ResetColor()
	{
		Console.ForegroundColor = ConsoleColor.Gray;
	}
}

Our goal is when we ask for IWeatherStation, Ninject should give us an implementation of that with all the dependencies created automatically.

For that first we have to bind the interfaces with implementations, in our case we have to bind the IWeatherStation with WeatherStation and IDisplay with ConsoleDisplay. We can do that through two ways either directly using the Bind methods of Kernel or creating a module and passing it to the Kernel. In our example we are going to use the first option but in big applications it is a good idea to bind the things through modules and pass them to the Kernel.

We have to bind the things in the composition root of the application. The composition root is different in different applications, in console applications it is the Main() method.

First create an instance of the StandardKernel.

var kernel = new StandardKernel(); 

Map the interfaces with concrete types. We can easily map the IDisplay interface with the ConsoleDisplay using the Bind and To methods.

kernel.Bind<IDisplay>().To<ConsoleDisplay>(); 

While mapping the IWeatherStation to the WeatherStation we need to pass both an IDisplay implementation and collection of WeatherConsition instances to the constructor. For the IDisplay implementation Ninject automatically passes the ConsoleDisplay instance. But for the weatherConditions parameter we have to pass it manually using the WithConstructorArgument method. The WithConstructorArgument takes both the parameter name and value as arguments.

kernel.Bind<IWeatherStation>().To<WeatherStation>().WithConstructorArgument
(
	"weatherConditions",
	new WeatherCondition[] 
	{ 
	new WeatherCondition{ Name = "HOT", Color = "Yellow" }, 
	new WeatherCondition{ Name = "COLD", Color = "Blue" }, 
	new WeatherCondition{ Name = "STORM", Color = "DarkGray" }, 
	new WeatherCondition{ Name = "SNOW", Color = "White" }, 
	new WeatherCondition{ Name = "WINDY", Color = "Gray" }
	}
);

The setup code is complete. Now we can get an instance of IWeatherStation implementation through the Get method of the kernel at any place.

var weatherStation = kernel.Get<IWeatherStation>();
weatherStation.DisplayReport();

If everything is working fine we will see a random weather condition displayed in the console.

Download Sample

blog comments powered by Disqus