Friday, August 08, 2008

Use Castle Windsor as IoC container in Microsoft.MVC

 

There are already some articles talking about how to use Unity IoC and StructureMap in Microsoft.MVC. I've used Windsor container for quite a while. Here I want to show you how to add the Castle Windsor IoC Container to an ASP.NET MVC Framework Application to create controllers and inject their dependencies.

1. First, let's implement our WindsorContainer, here we add each MVC controller as normal Component.

// *********************
// WebContainer.cs
// *********************

using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using Castle.Core.Resource;

using Shop.Frontend.Controllers;

namespace Duplium.Shop.Frontend
{
    public class WebContainer : WindsorContainer
    {
        public WebContainer() //: base(new XmlInterpreter(new ConfigResource()))
        {
            AddFacilities();
            AddComponents();
        }

        private void AddFacilities()
        {

        }

        private void AddComponents()
        {
            base.AddComponent<DemoProductsDataContext>();

            base.AddComponent<ProductController>();
            base.AddComponent<HomeController>();
        }
    }
}

 

2. Then, implement a ControllerFactory class. It'll be used and replace MVC default controller factory.

// *********************
// ControllerFactory.cs
// *********************

using System.Web.Mvc;

using Castle.Core;
using Castle.Windsor;

namespace Shop.Frontend
{
    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            if (controllerType == null)
                return (IController)null;

            IWindsorContainer container = null;
            try
            {
                container = ((GlobalApplication)(this.RequestContext.HttpContext.ApplicationInstance)).Container;
                return (Controller)container.Resolve(controllerType);

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }

    }
}

3. At last, assembly them together in Global.asax

 

// *********************
// Global.asax.cs
// *********************

using Castle.Core;
using Castle.Windsor;

namespace Shop.Frontend
{
    public class GlobalApplication : System.Web.HttpApplication, IContainerAccessor
    {
        private static IWindsorContainer container;

        #region IContainerAccessor

        public IWindsorContainer Container
        {
            get { return container; }
        }

        #endregion

        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()
        {
            container = new WebContainer();
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(
                new ControllerFactory()
                );
        }

        protected void Application_End(object sender, EventArgs e)
        {
            container.Dispose();
        }
    }
}