Over the last several years, as software development processes have matured a number of best practice postulates have been realized and then formalized. One of the famous ones goes by the acronym SOLID proposed by Robert C. Martin (fondly known as Uncle Bob Martin). SOLID stands for Single responsibility, Open closed principle, Liskov substitution principle, Interface segregation principle and Dependency Inversion.
Dependency Injection is a way to achieve Dependency Inversion and Inversion of Control (IoC) containers are frameworks that help us implement Dependency Injection. Today, we will look at Ninject (an IoC container) in a sample ASP.NET MVC Web application and understand the basics of DI using IoC.
This article is published from the DNC Magazine – A Free High Quality Digital Magazine for .NET techies published once every two months.
This month's edition features hot topics like ASP.NET MVC4, SignalR, Knockout.js, jsRender, TDD, Visual Studio ALM, HTML5, SharePoint, Windows Azure and Metro Applications amongst others. Not to mention, a freewheeling interview with Ayende Rahien, the man behind RavenDB.
Download this Free Magazine Here
Before we jump into Ninject and Dependency Injection, let us quickly run through the basics of DI. The basic premises of DI are
- All dependencies of a class should be passed (injected) into the class instead of being instantiated directly. In other words avoid new-ing up of interdependent layers.
- Code to Interfaces instead of concrete implementations.
- All Class instantiation and object lifecycle management should be done at a central location referred to as the ‘Composition Root’.
- Inversion of Control (IoC) Containers refers to frameworks that help manage the object instantiation and lifecycle.
- You can do DI without IoC containers, in such cases the pattern is referred to as ‘poor-man’s-DI’ and you have to hand code the object instantiation and lifecycle management.
A Simple Application
For the purposes of our discussion, we will take a simple three-layer application with one Entity on which we will be doing Create Read Update and Delete (CRUD) operations. A standard layering scheme looks like the following
But in such a scheme, each layer instantiates the layer above it and the View layer access the Data Access Layer too. This is classic example of a hard coupled system.
Instead of the above, if we define the Data Access Interfaces in our Domain layer and implement those interfaces in the Data layer, the dependencies would get inverted and Data Layer would then be dependent on the Domain layer. The View layer continues to refer to the Domain layer. Since View layer doesn’t have access to EntityFramework or the Data Access Objects, how does it deal with the data? We define a layer of Plain Old CLR Objects (POCOs) in the Domain Layer. These POCOs are our Data Transfer Object (DTOs) between Data and Domain layers.
Since the Domain layer only defines Interfaces to the data layer (aka Repository Interfaces), there is nothing to new up in the View layer. The concrete instance of the Domain layer is passed in to the Domain layer. The concrete instance is created in the Composition Root.
Now our application layering looks as follows:
How does this translate into code?
Well the sample application has the following structure.
The Data Layer
This layer has
- POCOs used for serialization/deserialization of data from DB, using the EntityFramework. If case of other OR/M it will contain the Data Objects required by the OR/Ms.
- The concrete implementation of the Repositories defined in the Domain Layer
- The translation layer from Domain object to Data object and vice versa.
The Domain Layer
This layer has
- The Interface definitions for all data access. Only the interfaced that are defined here is accessible to the view layer
- The Domain POCOs
- A Service Layer to manage business rules if any.
The View Layer
- The view markup
- The rendering logic
- The Composition Root. For purposes of simplification, the Composition Root is shown in the View layer but as we will see later, this gives rise to a coupling that we will eventually have to break.
A Closer Look at the Composition Root
Before we jump into an IoC container, lets take a closer look at the Composition Root.
The Composition Root is essentially determining the concrete instance (SqlBlogPostRepository) for an interface (IBlogPostRepository), instantiating the concrete instance.
Next the code is fetching an instance of IControllerFactory that is responsible for generating all the controllers required in the MVC application. The Composition Root is also injecting the repository into the Controller Factory instance, thus making the Repository available for use in the View layer.
For a simple application as this sample, this discovery instantiating and injection looks trivial enough, but imagine a project with multiple controllers and repositories and other related dependencies, trying to track all of those manually and wiring them in the composition root will soon become a nightmare and devs in the team will start trying to hack their way around. This is where Inversion of Control Containers (or IoC Containers) comes into picture. IoC containers when configured to map an interface to a concrete type, can generate a new concrete instance whenever required. This automates the process of dependency wiring. As long as everyone is referring to Interfaces and the IoC container knows how to get a concrete class for that Interface, we are good. Ninject is once of the newer and popular IoC containers. It’s an open source project that is actively worked upon and has a vibrant community.
Getting Started with Ninject
Now that we have seen the code factored to use Poor Man’s DI, we will see how we can nicely transition to using Ninject and see the challenges on the way.
- Select the web project
- Open the (Nuget) Package Manager Console and use the following commands
This installs the required dependencies for Ninject in an MVC application.
‘Configuring’ Ninject to be your IoC Container
Step 1: Open Global.asax and change the subclass from System.Web.HttpApplication to Ninject.Web.Common.NinjectHttpApplication
Step 2: Override the ‘CreateKernel’ method from the NinjectHttpApplication class in Global.asax and add the following
Step 3: Mapping the Dependencies.
- Add reference to FunWithSignalR.Data to your composition root (currently this is the web project don’t worry about the seemingly backwards dependency coupling, we will decouple it later).
- Create a new Class called DependencyMapper and inherit it from NinjectModule
- Override the Load() method and map the IBlogPostRepository to SqlBlogPostRepository as follows:
This code tells Ninject that whenever IBlogPostRepository is requested for, instantiate SqlBlogPostRepository with the given arguments in its constructor.
This method is where we will map similar dependencies when our project grows in size.
- Note how we pass constructor arguments to Ninject so that Ninject can use them at the time of type initialization.
- Setup the Application Start. Comment out the Application_Start() event’s code and add an override for the OnApplicationStarted method in the Global.asax
- If you run your application now, it will be up and running fine using Ninject IoC Container! W00t!
That covers up our migration from Poor Man’s DI to using Ninject as your IoC Container in ASP.NET MVC. For a sample project like ours, the benefits seem negligible but for larger projects having an IoC Container, the benefit goes a long way in helping manage object instantiation, Interface based development and therefore loosely coupled apps.
But… you just bunched all the references into the View layer!
Yes, you are right, and it’s a valid objection. If using IoC Containers and DI is the holy grail of decoupled application development, we just shattered that concept. The answer is rather simple. The ‘Composition Root’ of your application HAS to have access to all possible dependencies. Come to think of it, it’s a reasonable expectation. If it is going to be responsible for wiring up Interfaces to Concrete instances, it SHOULD know where to find the concrete instances.
IoC Containers support various ‘discovery modes’ for concrete instances. They are roughly bunched into
- XML Configuration
- Configuration via code conventions
- Configuration via code
What we saw was Configuration via code. Ninject does not support an XML Configuration directly. The Extensions project help in that regard. We are not looking at these extensions today.
To solve our problem of having all references in the View layer, we will create a ‘Bootstrap’ project and add all references to that project. Then call the bootstrap from our Application start up. This way we can avoid the referencing issues.
Moving ‘Composition Root’ out of the Web Project
To solve our architectural issue of View layer referring to Data layer directly, we split the code up as follows:
1. Add a new ClassLibrary project and call it FunWithSignalR.CompositionRoot
2. Remove the default Class1.
3. Remove EntityFramework, FunWithSignalR.Data dependencies from the FunWithSignalRDI.Web project and add FunWithSignalR.Data and FunwithSignalRDI.Domain to the FunWithSignalR.CompositionRoot.
4. Add Ninject dependencies to the CompositionRoot project by using the following command
Note you don’t need Ninject.MVC3 package here.
5. Move DependencyMapper.cs to the CompositionRoot project and update Namespaces are required
6. Add references to FunWithSignalR.Data, FunWithSignalR.Domain to the CompositionRoot
7. Add reference to System.Configuration.dll to the CompositionRoot.
8. Back in the Global.asax of the Web project, update the CreateKernel() method to include the CompositionRoot.
9. Remove the old BlogControllerFactory and CompositionRoot classes from the Web project.
10. Run the application. Voila! We have Ninject + ASP.NET MVC going like a charm.
We just scraped the surface of IoC Containers, specifically Ninject. We just resolved our repository types using the container. We could potentially extract interfaces for our Domain Entities and get the container to resolve them for us too.
Utility of the IoC Containers becomes quickly evident in very large projects.
For our well-experienced readers, hope this was a sufficient ‘quick-summary’. For our readers getting into DI and IoC we hope to have provided you with enough nudge to first adopt DI and next use IoC containers as a regular development practice.
Download the source code