Introduction
Recently I’ve been started a new MVC 3 app which is to have lot’s of components so I decided to go down the Dependency Injection route. This, for those who are hazy, is a OO design pattern for decoupling components.
I did some research (read, Google) and thought I’d go with StructureMap, but due to my limitations at work I must use VB.NET and I couldn’t find many examples, even converting from C# using my brain and auto tools, so I had some issues getting it working exactly how I wanted it to. Therefore I decided to try Ninject. The differences aren’t much and both do the DI I wanted to do but Ninject I got working in less than 10 minutes.
The app I’m working on is a MVC3 e-Commerce app with several distinct areas, such as categories, products, content pages, etc. Using the repository pattern I have repositories for returning the models but the actual concrete implementation of this can be done however I want and Ninject takes care of returning the correct concrete implementation. This means the application can be built with simple “fake/dummy” implementations and the whole site can be designed without ever needing to do the actual behind the scene complexities.
I will also use Ninject for returning concrete implementations of logging functionality, accounts, baskets, etc.
Simple “How I Did It”
The thing that frustrated me was trying to find a good example that was simple and easy to reimplement for myself so hopefully this will help for those with similar software architecture:
- Download Ninject - http://ninject.org/
- Add a reference to the MVC Web Project for Ninject.dll
- Create a class inheriting from the default MVC controller factory
Imports System.Configuration Imports System.Web.Mvc Imports System.Web.Routing Imports Ninject Imports Ninject.Modules Imports makit.Core.Repositories Namespace Infrastructure Public Class DIControllerFactory Inherits DefaultControllerFactory Private kernel As IKernel = New StandardKernel(New DIServices()) ' MVC calls this to get the controller for each request' Protected Overrides Function GetControllerInstance(ByVal context As RequestContext, ByVal controllerType As Type) As IController If controllerType Is Nothing Then Return Nothing End If Return DirectCast(kernel.[Get](controllerType), IController) End Function ' Configures how abstract service types are mapped to concrete implementations' Private Class DIServices Inherits NinjectModule Public Overrides Sub Load() Bind(Of Abstract.IProductRepository)().[To](Of Concrete.XMLProductRepository)() Bind(Of Abstract.ICategoryRepository)().[To](Of Concrete.XMLCategoryRepository)() End Sub End Class End Class End Namespace - As can be seen in the example above I am specifying to use the concrete XML repository class where ever the repository interfaces are referenced. An example of this use is:
Namespace Controllers Public Class ProductController Inherits System.Web.Mvc.Controller Private m_ProductsRespository As IProductRepository Public Sub New(ByVal prodRepos As IProductRepository) m_ProductsRespository = prodRepos End Sub Function Index() As ActionResult Return View() End Function End Class End Namespace - Now we need to add in the code that will get the dependency to fire, this is done with a line in the Application_Start function (in Global.asax):
Sub Application_Start() AreaRegistration.RegisterAllAreas() RegisterGlobalFilters(GlobalFilters.Filters) RegisterRoutes(RouteTable.Routes) ' The Important line below' ControllerBuilder.Current.SetControllerFactory(New DIControllerFactory()) End Sub - This now means when the application starts in IIS Ninject will sort out the dependencies based on the code in DIServices, such as in the case of the ProductController example above. The main example used here is so that the products & categories for an eCommerce site can come from any type of repository - XML, SQL Server, MYSQL, Magic, etc.
Hopefully this will help you setup dependency injection in your MVC app. If you wish to see how I’ve got the repositories setup then here are my base class/intertface setups:
Namespace Repositories.Abstract
Public Interface IProductRepository
Function getProduct(ByVal id As String) As Models.Product
End Interface
End Namespace
Imports System.IO
Namespace Repositories.Concrete.XML
Public Class XMLProductRepository
Implements Abstract.IProductRepository
Public Function getProduct(ByVal id As String) As Models.Product Implements Abstract.IProductRepository.getProduct
Dim prodMdl As Models.Product = Nothing
Dim x As New System.Xml.Serialization.XmlSerializer(GetType(Models.Product))
Dim fileName As String = Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/Products/"), id & ".xml")
If File.Exists(fileName) Then
Using oStmR As New StreamReader(fileName)
prodMdl = CType(x.Deserialize(oStmR), Models.Product)
oStmR.Close()
End Using
End If
Return prodMdl
End Function
End Class
End Namespace