Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using Munq IOC with ASP.NET MVC 2 Preview 2

0.00/5 (No votes)
23 Jan 2010 1  
In this article, I will walk through the modification of the default ASP.NET MVC 2 application to use the Munq IOC container.

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:

    // This constructor is used by the MVC framework to instantiate the controller using
    // the default forms authentication and membership providers.

    public AccountController()
        : this(null, null)
    {
    }

    // This constructor is not used by the MVC framework but is instead provided for ease
    // of unit testing this type. See the comments at the end of this file for more
    // information.
    public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
    {
        FormsAuth = formsAuth ?? new FormsAuthenticationService();
        MembershipService = service ?? new AccountMembershipService();
    }

To ‘fix’ this, you need to:

  1. Add a reference to the Munq.DI.dll.
  2. In the global.asax:
    1. Create an application level Container variable
    2. Register the dependencies in the container on Application_Start
  3. Change the default constructor for AccountController to resolve the dependencies
  4. 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
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    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",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                 new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );

        }

        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
{

    // This constructor is used by the MVC framework to instantiate the controller using
    // the default forms authentication and membership providers.
    public AccountController()
        : this(    MvcApplication.IOC.Resolve<IFormsAuthentication>(),
          MvcApplication.IOC.Resolve<IMembershipService>())
    {
    }

    // This constructor is not used by the MVC framework but is instead provided for ease
    // of unit testing this type. See the comments at the end of this file for more
    // information.
    public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
    {
        // Validate the parameters
        if (formsAuth == null)
            throw new ArgumentNullException("formsAuth");

        if (service == null)
            throw new ArgumentNullException("service");

        // set the dependencies
        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:

  1. Create a new class library project
  2. Add references to Munq.DI, System.Web.MVC, and System.Web.Routing
  3. Create the MunqControllerFactory class
  4. Register the new Controller Factory and register the controllers
  5. Remove the dependency on the Container from AccountController
  6. Fix up the tests

After steps 1-3, the project should look like:

clip_image001[4]

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
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    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",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            InitializeIOC();
            RegisterRoutes(RouteTable.Routes);
        }

        private void InitializeIOC()
        {
            // Create the IOC container
            IOC = new Container();

            // Create the Default Factory
            var controllerFactory = new MunqControllerFactory(IOC);

            // set the controller factory
            ControllerBuilder.Current.SetControllerFactory(controllerFactory);

            // Register the dependencies
            IOC.Register<>IFormsAuthentication>(ioc => new FormsAuthenticationService());
            IOC.Register<IMembershipService>(ioc => new AccountMembershipService());

            // Register the Controllers
            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)
    {
        // Validate the parameters
        if (formsAuth == null)
            throw new ArgumentNullException("formsAuth");

        if (service == null)
            throw new ArgumentNullException("service");

        // set the dependencies
        FormsAuth = formsAuth;
        MembershipService = service;
    }
...

The only thing left is to comment out the AccountController Unit Test which tests the constructor we just removed.

...
        //[TestMethod]
        //public void ConstructorSetsPropertiesToDefaultValues()
        //{
        //    // Act
        //    AccountController controller = new AccountController();

        //    // Assert
        //    Assert.IsNotNull(controller.FormsAuth, "FormsAuth property is null.");
        //    Assert.IsNotNull(controller.MembershipService, "MembershipService property is null.");
        //}
...

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: ,,,

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here