Ninject – Dependency Injection and Inversion of Control
A lot advanced programmers certainly have heard of this two big words, Dependency Injection (DI) & Inversion of Control (IoC). Mainly known as very desirable pattern for solutions on software projects because they enable a nice and clean architecture and loosely cloupling between the various software components.
But before I start giving a big speech about DI & IoC, first I’ll start with a pratical example, using C# (because Ninject was implemented for .NET), to explain what really are this big two words. Then I’ll talk about the practical application of Ninject, and the importance of these two patterns, and also give a little example on how to use the Ninject Extension MVC, to allow the usage of this two patterns on your ASP.NET MVC application. So let’s start with the example…
WarriorGame
Imagine that someone asked you to develop a simple console-based game, and the only requisites are:
- a Samurai is able to attack using a Sword
Quite simple? So lets write some code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| public class Sword { public void Hit( string target) { Console.WriteLine( "Chopped {0} clean in half." , target); } } public class Samurai { private readonly Sword _sword; public Samurai() { _sword = new Sword(); } public void Attack( string target) { _sword.Hit(target); } } public class Program { static void Main() { Samurai samurai = new Samurai(); samurai.Attack( "bad programmers" ); Console.ReadLine(); } } |
Simple isn’t it? now imagine that your little program had a great success and someone asked you to add the possibility of the Samurai use the Shuriken. Very well, lets implement a Shuriken.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| public class Shuriken { public void Hit( string target) { Console.WriteLine( "Pierces the {0}'s armor." , target); } } public class Samurai { private readonly Sword _sword; private readonly Shuriken _shuriken; private readonly bool _useSword; public Samurai( bool useSword) { _sword = new Sword(); _shuriken = new Shuriken(); _useSword = useSword; } public void Attack( string target) { if (_useSword) _sword.Hit(target); else _shuriken.Hit(target); } } public class Program { static void Main() { Samurai samurai = new Samurai( false ); samurai.Attack( "bad programmers" ); Console.ReadLine(); } } |
To those worried about this really bad code, don’t worry I done this on propose so I can explain the DI & IoC. The boolean on the Samurai constructor is a really bad solution, mainly because it is not very intuitive. Of course this design has a problem, and that is it doesn’t consider the possibility of adding new weapons on the future, to solve this problem I will use the DI and at the same time IoC to achieve a more beautiful and extensible design.
First if you have been paying attention you must had seen that both Sword and Shuriken share the same behaviour, and if you know or have hear of the SOLID principles of OO programming, the Dependency Inversion Principle states that a implementation must be decoupled from its dependencies implementations, in most OOP languages this is solved using a interface. How in this practical example do I do that? Simple, just define a IWeapon interface that abstracts the implementation of Hit method.
So let implement this next solution.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public interface IWeapon { void Hit( string target); } public class Sword : IWeapon { public void Hit( string target) { Console.WriteLine( "Chopped {0} clean in half." , target); } } public class Shuriken : IWeapon { public void Hit( string target) { Console.WriteLine( "Pierces the {0}'s armor." , target); } } |
Now as you can imagine the code on the Samurai class will be much more simple, clean, and more readable. And here is the spot here I will use the Dependency Injection Pattern on the Samurai class so that the instance is able to choose at the time of construction the type of weapon required.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| public class Samurai { private readonly IWeapon _weapon; public Samurai(IWeapon weapon) { _weapon = weapon; } public void Attack( string target) { _weapon.Hit(target); } } public class Program { static void Main() { Samurai samurai = new Samurai( new Sword()); samurai.Attack( "bad programmers" ); Console.ReadLine(); } } |
Was you can see the Samurai now can operate a weapon, any kind of weapon, with this my application can grow in a more natural way and even can grow without the need to recompile all the code. The Dependency Injection Pattern on the Samurai constructor allows me to inject a weapon for the Samurai to use.
With this example I also used the Inversion of Control Pattern, because at the construction of any instance of Samurai, it is obligated to pass on its constructor the instance that implements IWeapon. On the first examples, the Samurai class had the Control of the Sword and Shuriken class, because it was responsible for the instantiation of both classes. With this implementation the Control is inverted so that the instantiation of all the dependencies could be managed on a centralize point.
Still this solution has quite a tricky problem, and that is the Manual Dependency Injection as you can see on the following line.
1
| Samurai samurai = new Samurai( new Sword()); |
This is quite the problem when we consider a big project with hundreds and thousands of instances and classes, you’ll have to glue all together by hand. That is not quite a desirable solution. And this is where Ninject make your life much more easy, but before I give you example of usage, lets talk a little about Ninject.
Ninject is a dependency container and injector that allow to automatize this whole process. It was originally developed by Nate Kohari (Ninject 1), currently on version 3. Ninject was become one of the most widely used DI containers, mainly because is a small but powerful piece of software and a open source project. Also Ninject has a lot of extensions that suite most project requirements. Also is very simple to use, so that you don’t need to be a wizard to use Ninject. Another great feature of Ninject is the fluent language configuration, in contrast with the usual XML configuration used in another DI containers, this allow a strongly typed configuration with less space for configuration errors. So where to start? well first here are some ways you can get/install ninject into your project:
- Ninject – http://www.ninject.org
- GitHub – http://github.com/ninject
- NuGet – Visual Studio Package Manager
How it works and How to use it?
Ninject features are centralized in a Kernel, and it must obey the contract on the IKernel interface. The implementation for this interface is on the type StandardKernel, and like the name suggest is the standard implementation for the Ninject Kernel. In this kernel you can configure the list of type bindings and also request the instance of a particular type. But I’m sure a practical example is more simple to show the power of Ninject. I will use the previous example, and so I have added the interface IWarrior to abstract the concept of Warrior, so my application can support new types of warriors.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| public interface IWarrior { void Attack( string target); } public class Samurai : IWarrior { private readonly IWeapon _weapon; public Samurai(IWeapon weapon) { _weapon = weapon; } public void Attack( string target) { _weapon.Hit(target); } } public class Program { static void Main() { IKernel kernel = new StandardKernel(); kernel.Bind<IWarrior>().To<Samurai>(); kernel.Bind<IWeapon>().To<Sword>(); IWarrior samurai = kernel.Get<IWarrior>(); samurai.Attack( "bad programmers" ); Console.ReadLine(); } } |
As you can see the usage of Ninject is highly fluent and easy to use. With the method Bind, I specify the type of the service/interface and then with the method To, I bind the service to its implementation. With the configuration I gave to Ninject the next time someone requests the kernel a instance of some type of service, it will give me its implementation. To get this instance you can use the method Get and request the service type, Ninject will figure out all the dependencies and inject them, until the hole service is built.
On this example, I request a IWarrior service, Ninject figures (because I have specified in the configuration) that a implementation available to IWarrior is the class Samurai. When looking for the constructor of Samurai to generate a instance, Ninject will have a dependency to resolve, and that is a dependency on the service IWeapon. Since we configured that when requested for a service of type IWeapon, Ninject must use Sword, the Samurai will receive a Sword as his Weapon. Since Sword class has no more dependencies, Ninject will retrieve an instance of IWarrior on the next order:
On this example, I request a IWarrior service, Ninject figures (because I have specified in the configuration) that a implementation available to IWarrior is the class Samurai. When looking for the constructor of Samurai to generate a instance, Ninject will have a dependency to resolve, and that is a dependency on the service IWeapon. Since we configured that when requested for a service of type IWeapon, Ninject must use Sword, the Samurai will receive a Sword as his Weapon. Since Sword class has no more dependencies, Ninject will retrieve an instance of IWarrior on the next order:
At the end the Get method will return a new instance of Samurai with a instance of Sword injected into it. Still there is a little problem, as you can see there are two parts to this code, configuration and usage, of course on a big project you will want to separate those two parts. Well Ninject offer a nice and clean way to do that, using a implementation of INinjectModule, the class NinjectModule. The usage of this class is quite simple, you only have to create a subclass of NinjectModule and implement the abstract method Load and define on this one all the binding for your module. The NinjectModule is not a name randomly chosen, the idea of these modules are to separate your application on Modules and them glue them all together on the Kernel. So if you have lots of layers on your application, you can create a module for each layer and them glue them together on the application top layer. So lets show how to use this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| public class WarriorModule : NinjectModule { public override void Load() { Bind<IWarrior>().To<Samurai>(); Bind<IWeapon>().To<Sword>(); } } public class Program { static void Main() { IKernel kernel = new StandardKernel( new WarriorModule()); IWarrior samurai = kernel.Get<IWarrior>(); samurai.Attack( "bad programmers" ); Console.ReadLine(); } } |
Quite nice, don’t you think? Now you have the basics to use Ninject in a nice and elegant way. Still there are just a few topics I would like to talk.
First the criteria when choosing which constructor to use when a type has multiple constructors. Well the criteria is quite simple actually, Ninject looks for the public constructor with the greater number of parameters that can be solved by the bindings present on the Kernel. But imagine that you want to force the usage of a specific constructor, there are two ways to do this:
- Decorate the constructor of the class with the Attribute [Inject].
- On Ninject 3 was introduced the method ToConstructor that allows you to declare the constructor to use, at the time of configuration.
Another nice feature of Ninject is the Scope instantiation, where you have these 4 main scopes:
- Transient Scope(default). At every request a new instance is created.
- Singleton Scope. Is instantiated only one time, will remain alive until the process is terminated.
- Thread Scope. An instance is created for each thread.
- Request Scope. An instance is created for each Web Request, mostly used on Web Applications. (on the Web.Common Extension of Ninject)
Also another quite nice and simple feature of Ninject is AutoWiring. This allows to obtain an instance of a type that isn’t declared on the configuration.
ASP.NET MVC Integration
The main reason why I’m doing this post is because of the integration of Ninject with ASP.NET MVC Projects, this can be done in a simple way. For example, by extending the DefaultControllerFactory and use Ninject to instantiate the controllers, you can define dependencies on the controller constructor and with this allow Ninject to inject any kind of services you need inside the controller. Here is an example on how to do it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public class NinjectControllerFactory : DefaultControllerFactory { private readonly IKernel _ninjectKernel; public NinjectControllerFactory() { _ninjectKernel = new StandardKernel(); /* .... */ } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController) _ninjectKernel.Get(controllerType); } } |
Another way, and much more easy, is use the Ninject.MVC3 Extension. You can get it on GitHub, or if you remember NuGet, on the last image right next to the Ninject package you can find this extension. If you install the MVC Extension right away, NuGet will first get the other Ninject dependences. To use this on your project simply go to the Global.asax.cs file and edit your HttpApplication:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class MvcApplication : NinjectHttpApplication { protected override IKernel CreateKernel() { var kernel = new StandardKernel( new SomeModule(), new SomeOtherModule()); //OR kernel.Load( new SomeModule(), new SomeOtherModule()); kernel.Load(Assembly.GetExecutingAssembly()); kernel.Load( "./modules1/" , "./modules2/" ); return kernel; } } |
With this usage you only need to return the kernel used by the NinjectHttpAplication, this class already applies a factory for the controllers, and also applies integration with factories of others MVC components, for example the MVC Filters.
I hope that with this post you will be able to use the simple feature of Ninject, on the way it was intended to be used. Also be aware of good practices on software design by using Dependency Injection and Inversion of Control Patterns. Please feel free to ask questions, and also point something out that I might have forgotten, or you would like to see a more detailed explanation. If you want to see the code developed on this post, feel free to look and fork my GitHub Repository for this post. On the repository you’ll also find a Presentation in Portuguese, about Ninject done by me for a academic presentation. If you would like to get an English version of my apresentation, please comment this post and I’ll think about it.
Until next time.
No comments:
Post a Comment