3 Feb 2011
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.
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
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
Sub Application_Start() AreaRegistration.RegisterAllAreas() RegisterGlobalFilters(GlobalFilters.Filters) RegisterRoutes(RouteTable.Routes) ' The Important line below ControllerBuilder.Current.SetControllerFactory(New DIControllerFactory()) End Sub
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