Overview
In this article, I will walk through the modification of the default ASP.NET MVC 2 application to use the Munq IOC container. This is a fairly simple process during which we will create a custom Controller Factory for the framework which you can use in other applications.
The previous article is available on my blog at Introduction to Munq IOC Container for ASP.NET or on The Code Project at Introduction to Munq IOC Container for ASP.NET. This article develops the base application from which we will examine the features of the Munq IOC in future articles.
Step 1: Create an MVC 2 project
Open Visual Studio and create a new MVC 2 application. Give it any name. I called mine FinalApp to distinguish it from the InitialApp I created as a reference.
Build the application and familiarize yourself with the login/register/logout functionality of the AccountController. Hint: login is available in the upper right of the page.
Step 2: Remove the dependencies in AccountController
The
AccountController has two hard dependencies to concrete implementations to
FormsAuthenticationService and
AccountMembershipService as shown in the
bold lines below.
public AccountController()
: this(null, null)
{
}
public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}
To ‘fix’ this you need to:
- Add a reference to the Munq.DI.dll.
- In the global.asax
- create an application level Container variable.
- Register the dependencies in the container on
Application_Start
- Change the default constructor for
AccountController to resolve the dependencies.
- Change the constructor with parameters to check for null arguments.
Registering and Resolving the Controllers will be handled later.
After steps 1 and 2, the global.asax should look like the listing below. Changes are highlighted.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Munq.DI;
using FinalApp.Controllers;
namespace FinalApp
{
public class MvcApplication : System.Web.HttpApplication
{
public static Container IOC {get; private set;}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" } );
}
protected void Application_Start()
{
IOC = new Container();
RegisterInterfaces();
RegisterRoutes(RouteTable.Routes);
}
private void RegisterInterfaces()
{
IOC.Register<IFormsAuthentication>( ioc => new AccountMembershipService());
IOC.Register<IMembershipService>( ioc => new FormsAuthenticationService());
}
}
After steps 3 and 4, the AccountController constructors should look like:
[HandleError]
public class AccountController : Controller
{
public AccountController()
: this( MvcApplication.IOC.Resolve<IFormsAuthentication>(),
MvcApplication.IOC.Resolve<IMembershipService>())
{
}
public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
if (formsAuth == null)
throw new ArgumentNullException("formsAuth");
if (service == null)
throw new ArgumentNullException("service");
FormsAuth = formsAuth;
MembershipService = service;
}
...
Now build and run the application. It should work as it did before we started.
Yes, it seems that we have done a lot of work to get the same functionality and traded two dependencies for a dependency on the IOC container. We will remove this dependency in the next step.
Step 3: Create an IOC aware ControllerFactory
In order to remove the dependency the controllers have on the IOC container, it is necessary to create a custom ControllerFactory. This ControllerFactory will use the IOC container to create the correct controller and resolve any constructor dependencies.
Since we will want to reuse this in future projects we will:
- Create a new class library project.
- Add references to
Munq.DI, System.Web.MVC, and System.Web.Routing.
- Create the
MunqControllerFactory class.
- Register the new Controller Factory and register the controllers.
- Remove the dependency on the Container from
AccountController.
- Fix up the tests.
After steps 1-3 the project should look like:
Because Munq can register factories by name, and Munq handles the caching of the factories and lookup performed by the DefaultControllerFactory class, we can derive a new MunqControllerFactory from the IControllerFactory interface. The one of the CreateController method’s parameters is the name of the controller, without the Controller suffix. This means we can register the controllers by name. The other method that needs to be written is the ReleaseController method, which disposes of the controller if required. This is shown below. Notice that the constructor take a Munq Container as a parameter.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using Munq.DI;
namespace Munq.MVC
{
public class MunqControllerFactory : IControllerFactory
{
public Container IOC { get; private set; }
public MunqControllerFactory(Container container)
{
IOC = container;
}
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
return IOC.Resolve<IController>(controllerName);
}
catch
{
return null;
}
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
}
}
The next step is to register the MunqControllerFactory as the default controller factory. Open up the global.asax and modify it as shown.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Munq.DI;
using Munq.MVC;
using FinalApp.Controllers;
namespace FinalApp
{
public class MvcApplication : System.Web.HttpApplication
{
public static Container IOC { get; private set; }
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" } );
}
protected void Application_Start()
{
InitializeIOC();
RegisterRoutes(RouteTable.Routes);
}
private void InitializeIOC()
{
IOC = new Container();
var controllerFactory = new MunqControllerFactory(IOC);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
IOC.Register<>IFormsAuthentication>(ioc => new FormsAuthenticationService());
IOC.Register<IMembershipService>(ioc => new AccountMembershipService());
IOC.Register<IController>("Home", ioc => new HomeController());
IOC.Register<IController>("Account",
ioc => new AccountController(ioc.Resolve<IFormsAuthentication>(),
ioc.Resolve<IMembershipService>())
);
}
}
}
Now we are ready to remove the dependency on the IOC from the AccountController. This is as simple as removing the default constructor as it references the IOC container. The MunqControllerFactory and the Munq IOC handle all the work of resolving the dependencies for the remaining constructor. The start of the AccountController should look like
...
[HandleError]
public class AccountController : Controller
{
public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
{
if (formsAuth == null)
throw new ArgumentNullException("formsAuth");
if (service == null)
throw new ArgumentNullException("service");
FormsAuth = formsAuth;
MembershipService = service;
}
...
The only thing left is to comment out the AccountController Unit Test which test the constructor we just removed.
...
...
Now you can build, run the tests and the application. We now have a platform where we can now demonstrate the different Lifetime Managers and how they can simplify state management for your application. That however is something for the next article. In the next set of articles, I will build a real-world application, what I haven’t decided so give me some ideas of what you would like to see.
Note: This version of the MunqControllerFactory does not support the Areas feature of MVC 2. This too will be corrected in a future article.
del.icio.us Tags: ASP.NET,MVC,IOC,DI
CodeProject
Matthew works on improving the performance and experience of the Code Project site for users, clients, and administrators.
Matthew has more years of software develeopment, QA and architecture experience under his belt than he likes to admit. He gradutated for the University of Waterloo with a B.Sc. in Electrical Engineering. He started out developing micro-processor based hardware and software including compilers and operating systems.
His current focus is on .NET web development including jQuery, Webforms, MVC, AJAX, and patterns and practices for creating better websites.
He is the author of the Munq IOC, the fastest ASP.NET focused IOC Container.
His non-programming passions include golf, pool, curling, reading and building stuff for the house.