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 tests 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