In his book Pro ASP.NET MVC Framework, author Steven Sanderson takes three chapters to explain how build an e-commerce application in ASP.NET MVC. Because Steven is a big fan of Domain-Driven Development (DDD) he uses the opportunity to explain how to apply the principles of Inversion of Control (IoC) or Dependency Injection (DI) using Castle Windsor, a IoC-framework with which you can quite easily introduce IoC to your application (if your business logic is modelled correctly of course).
Because I find the concept of DDD and IoC very interesting, I have decided to check out how easy it is to replace Castle Windsor in Steven’s example application with another IoC-framework: Ninject.
My first attempts with Ninject 1.0 didn’t lead to much. It quickly became too mmuch of a hassle compared to Castle Windsor. Just when I was about to give up, I noticed that there was beta version of Ninject 2.0 available. With this version it was a breeze to set up IoC.
To allow an ASP.NET MVC application work with Ninject 2.0 for DI, you can follow these easy steps:
Download Ninject and add it to your application
On Github, you can download the latest version of Ninject 2.0 and the Ninject.Web.MVC extension.
The only thing you have to do is download the source code of the two projects, compile them to get the two needed dll’s (Ninject.ddl and Ninject.Web.Mvc.ddl) and add these dll’s to your project.
Configurate Ninject
Now your project contains the references to Ninject, there are a few things to be configured. The bulk of these settings can be done in the code behind of your Global.asax.
The first thing you have to do is include the two Ninject libraries:
using Ninject;
using Ninject.Web.Mvc;
As you may notice, your Global.asax class is called MvcApplication and inherits by default from System.Web.HttpAplication:
public class MvcApplication : System.Web.HttpApplication
{
...
}
A big part of Ninject works automagically. This magic can be achieved by letting the MvcApplication class inherit from NinjectHttpApplication:
public class MvcApplication : NinjectHttpApplication
{
...
}
The Global.asax has an Application_Start() method. The NinjectHttpApplication class has its own version to serve that purpose and we are going to implement it now. Replace the Application_Start() with the following code:
protected override void OnApplicationStarted()
{
RegisterAllControllersIn(System.Reflection.Assembly.GetExecutingAssembly());
RegisterRoutes(RouteTable.Routes);
}
The first line is responsible for registering all controller classes (classes that implement IController somehow). The second line is simply copied from the old Application_Start() methid and takes case of route registration, just like in a default MVC application.
Now we have to initialize the Ninject kernel. This can be done with the following code:
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(System.Reflection.Assembly.GetExecutingAssembly());
return kernel;
}
It is the kernel that eventually takes care injecting the dependencies. To do this, the kernel uses a list of bindings. But where does it get this list? From all classes in your application that inherit from the NinjectModule class.
Creating a module with bindings
Now add a new class to your application and call it, for example, WebModule and let it inherit from NinjectModule (in the Ninject.Modules namespace) and implement the Load() method:
public class WebModule : NinjectModule
{
public override void Load()
{
Bind<DomainModel.Abstract.IProductsRepository>().To<DomainModel.Concrete.SqlProductsRepository>().WithConstructorArgument("connectionString", @"Server=.\SQLEXPRESS;Database=SportsStore;Trusted_Connection=yes;");
Bind<DomainModel.Services.IOrderSubmitter>().To<DomainModel.Services.EmailOrderSubmitter>().WithConstructorArgument("smtpServer", "127.0.0.1").WithConstructorArgument("mailFrom", "sportsstore@example.com").WithConstructorArgument("mailTo", "admin@example.com");
}
}
In this example what the Load() method looks like for the SportsStore application from the Pro ASP.NET MVC Framework book.
Every time the Ninject kernel encounters a controller with a contructor parameter of the type IProductsRepository, it will pass in an instance of SqlProductsRepository. This SqlProductsRepository also has a constructor parameter: the connectionstring. This connectionstring can also be passed in using Ninject bindings.
The same goes for the IOrderSubmitter. In this case the kernel will pass in an instance of lOrderSubmitter meegeven with three constructor parameters.
Conclusion
It was actually quite easy to replace Castle Windsor with Ninject 2.0. They are both easy to configure. What can be an advantage of Castle Windsor is that the bindings are done in the Web.Config. That can come in handy when you have separate environments for development, staging and production. In that case you can just use the same code in all three environments but with different IoC settings in the Web.Config files.
The above bindings look like this for Castle Windsor:
<configuration>
<configSections>
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</configSections>
<castle>
<components>
<component id="ProductsRepository"
service="DomainModel.Abstract.IProductsRepository, DomainModel"
type="DomainModel.Concrete.SqlProductsRepository, DomainModel"
lifestyle="PerWebRequest">
<parameters>
<connectionString>Server=.\SQLEXPRESS;Database=SportsStore;Trusted_Connection=yes;</connectionString>
</parameters>
</component>
<component id="OrderSubmitter"
service="DomainModel.Services.IOrderSubmitter, DomainModel"
type="DomainModel.Services.EmailOrderSubmitter, DomainModel">
<parameters>
<smtpServer>127.0.0.1
<mailFrom>sportsstore@example.com
<mailTo>admin@example.com
</parameters>
</component>
</components>
</castle>
</configuration>