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
|
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.
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.
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.
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.
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:
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:
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.
Excellent article. Very interesting to read. I really love to read such a nice article. Thanks! keep rocking.AngularJS 5 Online Course Hyderabad
ReplyDelete