Resolving Sitecore Controllers From Your IoC Container

EDIT: This post covers versions pre Sitecore 8.1.

In this post I will be describing pulling your controllers for use in controller renderings from an IoC container, in this case (again) Castle Windsor. The principle would apply to most containers in the same manner.

In my on-going quest to avoid the ‘new’ keyword in my Sitecore implementations, well – in fact more just to delegate the responsibility of dependency management to my IoC container. I remembered that we had pulled together from a few internet sources, a set of classes that allow the instantiation of Controllers from our IoC container. I wish I could credit the internet sources, but sadly as lazy developers, we gladly consumed and dutifully discarded them. I would also like to add that this was the collaboration of a couple of developers to finalise this solution, so not all my own handiwork on this occasion.

Firstly, we need to create ourselves a controller factory that allows us to return controllers from the IoC container.

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

using Castle.Windsor;

using Sitecore.Mvc.Helpers;

namespace MyNamespace
{
    public class WindsorControllerFactory : DefaultControllerFactory
    {
        private const int PageNotFoundStatus = 404;

        private readonly IWindsorContainer container;

        public WindsorControllerFactory(IWindsorContainer container)
        {
            this.container = container;
        }

        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            // todo: wrap this in the final implementation
            if (!TypeHelper.LooksLikeTypeName(controllerName))
            {
                return base.CreateController(requestContext, controllerName);
            }

            Type type = TypeHelper.GetType(controllerName);
            if (type != null)
            {
                return GetControllerInstance(requestContext, type);
            }
        }

        public override void ReleaseController(IController controller)
        {
            container.Release(controller);
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                // todo: the request context should be wrapped in final implementation.
                throw new HttpException(
                    PageNotFoundStatus , 
                    string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
            }

            return container.Resolve(controllerType) as IController;
        }
    }
}

Now we create an implementation of the Sitecore Controller Factory to wrap our default one, this handles the messages for controller requests.


using System.Web.Mvc;
using System.Web.Routing;
using Sitecore.Mvc.Controllers;
using Sitecore.Mvc.Extensions;

namespace MyNamespace
{
    public class WindsorSitecoreControllerFactory : SitecoreControllerFactory
    {
        public SitecoreWindsorControllerFactory(IControllerFactory innerFactory) : base(innerFactory)
        {
        }

        protected override IController CreateControllerInstance(RequestContext requestContext, string controllerName)
        {
            if (controllerName.EqualsText(SitecoreControllerName))
            {
                return CreateSitecoreController(requestContext, controllerName);
            }

            return InnerFactory.CreateController(requestContext, controllerName);
        }
    }
}

Now, we need to override the creation of the controller factory in Sitecore to use our WindsorControllerFactory in place of the default one. We do this by creating a simple pipeline entry similar to below.

using System.Web;
using System.Web.Mvc;
using Castle.Windsor;

using Sitecore.Pipelines;

namespace MyNamespace
{
    public class InitializeWindsorControllerFactory
    {
        public virtual void Process(PipelineArgs args)
        {
            SetupControllerFactory(args);
        }

        public virtual void SetupControllerFactory(PipelineArgs args)
        {
            IWindsorContainer container = ioc.GetContainer();
            if (container == null)
            {
                return;
            }

            WindsorControllerFactory controllerFactory = new WindsorControllerFactory(container);
            
            WindsorSitecoreControllerFactory sitecoreControllerFactory = new WindsorSitecoreControllerFactory(controllerFactory);
            ControllerBuilder.Current.SetControllerFactory(sitecoreControllerFactory);
        }
    }
}

We then patch this in using a configuration patch similar to the one below.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor patch:instead="*[@type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']" type="MyNamespace.InitializeWindsorControllerFactory, MyNamespace"/>
      </initialize>
    </pipelines>
  </sitecore

Additional Reading
Resolving Sitecore configuration entries using IoC with Named Components

Advertisements

4 thoughts on “Resolving Sitecore Controllers From Your IoC Container

  1. Hi Nat,

    very nice post thank you. But I asked me why you add a custom Sitecore Controller Factory implementation? Shouldn’t this work without this, as Sitecore resolves the controller via the default MVC controller factory you have already defined (at least when you don’t add the fullname of the class in the controller rendering definition item, but only the controller, i.e. “Home”)?

    Greets,
    Kevin

  2. I think its the TypeHelper logic that exists in the SitecoreControllerFactory (decompiled below).

    if (TypeHelper.LooksLikeTypeName(controllerName))
    {
    Type type = TypeHelper.GetType(controllerName);
    if (type != (Type) null)
    return TypeHelper.CreateObject(type);
    }

    TypeHelper.CreateObject uses Activator.CreateInstance which we don’t want. Our controller names for controller renderings we used are fully qualified.

  3. Pingback: Changes to Dependency Injection in Sitecore 8.1 | The Runtime Report

  4. Pingback: Safe Dependency Injection for MVC and WebApi within Sitecore | Sean Holmesby

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s