ASP.NET MVC 3: Agnostic Inversion of Control


ASP.NET MVC has matured over the years and I’ve had the fortune to be able to do some development work recently on this. One of the first things I explored was to setup an Inversion of Control (IoC) container. One of the biggest benefits of using an IoC is to accommodate for the ease of unit testing using a mocking framework.

Anyways we use Castle Windsor on our project, but in theory we should be able to use any IoC framework.  I started to think about how to decouple the project from a specific IoC framework, so if we ever wanted to change to a different one, it would be easy and straightforward. Perhaps this is overkill in the real world, but would be a good exercise to go through.

For this example, I’ve decided to use Castle Windsor and Unity, both of which I’m pretty familiar with. The idea behind this is to create an interface library on which the Castle and Unity libraries will implement, essentially a plug-in mechanism.

Here is the interface for IContainer, essentially a contract for the IoC Container we will be utilizing.

public interface IContainer : IRegister, IResolve, IDisposable
{
}

public interface IResolve
{
    T Resolve<T>();

    T Resolve<T>(string key);

    bool CanResolve<T>();

    bool CanResolve<T>(string key);

    void Release(object instance);
}

public interface IRegister
{
    IRegister Register<TInterface, TClass>(Lifetime lifetime)
        where TInterface : class
        where TClass : TInterface;

    IRegister Register<TInterface, TClass>(string key, Lifetime lifetime)
        where TInterface : class
        where TClass : TInterface;

    IRegister RegisterAllClassesBasedOn<TInterface>(Lifetime lifetime) where TInterface : class;
}

There’s also an interface for IInstaller, which is a contract for installing or registering your components. This is based on the installer concept in Castle Windsor which I find useful.

public interface IInstaller
{
    void Install(IContainer container);
}

Lastly there’s a IoC static class which allows you to register your selected IoC implementation based on the specification from a configuration section, like so.

<configSections>
    <section name="iocConfiguration" type="IoC.IocConfiguration, IoC"/>
</configSections>

<iocConfiguration assemblyName="Unity" />

 

public static class Ioc
{
    public static IContainer Initialize(IInstaller installer)
    {
        var assemblyWithLocation = GetAssemblyNameWithLocation();
        var assembly = Assembly.LoadFrom(assemblyWithLocation);

        var containerType =
            assembly.GetTypes().Single(x => x.GetInterface(typeof(IContainer).FullName) != null);
        var container = (IContainer)Activator.CreateInstance(containerType);
        installer.Install(container);
        return container;
    }

    private static string GetAssemblyNameWithLocation()
    {
        var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
        var assemblyNameWithLocation = Path.Combine(location, GetAssemblyName()).Replace("file:\\", "");

        if (!File.Exists(assemblyNameWithLocation))
            throw new FileNotFoundException("unable to find assembly");
        return assemblyNameWithLocation;
    }

    private static string GetAssemblyName()
    {
        return string.Format("{0}.dll", IocConfiguration.Settings.AssemblyName);
    }
}

You just have to specify the name of the assembly you want to plug in. You also have to ensure that the assembly is present in the folder containing your executing assembly, I achieved this using a post build event.

As for the implementation details of Castle and Unity, I won’t be going into details. You can download the sample to have a look if interested. In the MVC application, I’ve created an Installer to register the HomeController. If there are other dependencies you need, register them here.

public class Installer : IInstaller
{
    public void Install(IContainer container)
    {
        container.Register<IController, HomeController>("Home", Lifetime.Transient);
    }
}

Next I create a ControllerFactory that uses our IContainer to register dependencies, instead of the default one from MVC.

public class ControllerFactory : IControllerFactory
{
    private readonly IContainer _container;
    private readonly IControllerFactory _defaultControllerFactory;

    public ControllerFactory(IContainer container) : this(container, new DefaultControllerFactory())
    {}

    public ControllerFactory(IContainer container, IControllerFactory defaultControllerFactory)
    {
        _container = container;
        _defaultControllerFactory = defaultControllerFactory;
    }

    public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        if (_container.CanResolve<IController>(controllerName))
            return _container.Resolve<IController>(controllerName);
        return _defaultControllerFactory.CreateController(requestContext, controllerName);
    }

    public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        return _defaultControllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
    }

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

Last of all, we set everything up in the Global.asax application start method, like so.

public class MvcApplication : System.Web.HttpApplication
{
    private IContainer _container;

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
         routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });

         routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        _container = IoC.Ioc.Initialize(new Installer());
        var controllerFactory = new ControllerFactory(_container);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);

    }

    protected void Application_End()
    {
         _container.Dispose();
    }
}

So here we have an agnostic IoC container. In theory you can create pluggable libraries for any IoC framework and bootstrap that into your MVC application. The beauty of this is that the IoC framework becomes transparent to the MVC application, and it will always be dealing with it’s IContainer interface and not an external dependency.

If you are interested in how to implement Castle with ASP.NET MVC 3, see this link.

If you are interested in how to implement Unity with ASP.NET MVC 3, see this link.

Download code sample here.

One Response to “ASP.NET MVC 3: Agnostic Inversion of Control”

  1. Chris Melinn Says:

    Nice writeup


Leave a comment