Dive Deep Into MVC - IControllerFactory

In these series of posts, I'll talk about some ASP.NET MVC aspects. I chose IControllerFactory as first post. Btw, make sure you have a basic knowledge about ASP.NET MVC at least before reading. If so continue.

What I want to have at last?

What I'm after is a controller that supports dependency injection.

What's a controller factory?

In a word, controller factory finds, creates and releases an instance of IController for specified controller name. All of controller factory should implement IControllerFactory interface directly or indirectly that is as follows.

public interface IControllerFactory
{
    IController CreateController(RequestContext requestContext, string controllerName);
    void ReleaseController(IController controller);
}

ASP.NET MVC default controller factory class, named DefaultControllerFactory. IMO to make my own, simple and reliable, it's better to derive it from DefaultControllerFactory instead of implementing IControllerFactory.

Implementing

The following code demonstrated the members structure of my controller factory.

public class DIControllerFactory : DefaultControllerFactory
{
    // ctor
    public DIControllerFactory(IDictionary<Type, Type> mappings)

    // methods
    protected virtual object[] GetTypeDependencies(Type controllerType)

    // overriden members
    public override IController CreateController(RequestContext requestContext, string controllerName);
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType);
}

In this case, I added a parameter to the constructor to take a key/value pairs of dependent types. Also I created a marker attribute to mark controller's default constructor that should be used by controller factory. The completed source of controller factory is as follows.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace DICF.ControllerFactory
{

    public class DIControllerFactory : DefaultControllerFactory
    {

        private IDictionary<Type, Type> mappings;

        // ctor
        public DIControllerFactory(IDictionary<Type, Type> mappings)
        {
            if (mappings == null)
                throw new ArgumentNullException("mappings");

            this.mappings = mappings;
        }

        // methods
        protected virtual object[] GetTypeDependencies(Type controllerType)
        {
            ArrayList dependencies = new ArrayList();

            ConstructorInfo ctor = (ConstructorInfo)controllerType.FindMembers(
                MemberTypes.Constructor,
                BindingFlags.Public | BindingFlags.Instance,
                (m, c) => m.IsDefined((Type)c, false),
                typeof(DefaultConstructorAttribute)).FirstOrDefault();

            if (ctor != null)
            {
                foreach (ParameterInfo param in ctor.GetParameters())
                {
                    Type mappedType;
                    this.mappings.TryGetValue(param.ParameterType, out mappedType);

                    if (mappedType == null)
                    {
                        dependencies.Clear();
                        break;
                    }

                    dependencies.Add(Activator.CreateInstance(mappedType));
                }
            }

            return dependencies.ToArray();
        }

        // overriden members
        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
                throw new ArgumentNullException("requestContext");

            if (string.IsNullOrEmpty(controllerName))
                throw new ArgumentNullException("controllerName");

            return this.GetControllerInstance(requestContext, this.GetControllerType(requestContext, controllerName));
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null || !typeof(IController).IsAssignableFrom(controllerType))
                throw new HttpException(404, string.Empty);

            return (IController)Activator.CreateInstance(controllerType, this.GetTypeDependencies(controllerType));
        }

    }

}

You can find and download the completed source code at the end of the post.

Usage

Just I should change the default controller factory to my own. Thus, change the Application_Start method of the Global.asax as follows.

protected void Application_Start()
{
    ControllerBuilder.Current.SetControllerFactory(new DIControllerFactory(new Dictionary<Type, Type>() { { typeof(ICalculator), typeof(Calculator) } }));
}

And nothing else...

Download Source

Download full source code with a simple example from here...

blog comments powered by Disqus