Updated to include global filters.

Unfortunately the latest effort to add DI to MVC falls short in a number of ways. There are 3 areas in particular that are causing me pain:

  1. No distinction between infrastructure and a request.
  2. No container per request.
  3. No request container create and release mechanism.

In order to accommodate functionality specific to a request there would need to be a distinction between building up infrastructure related objects (created once at startup, e.g. IControllerFactory, IControllerActivator, etc.) and request related objects (created for each request, e.g. controllers, views, etc.) Unfortunately, all objects are built up from the same two methods regardless of if they're infrastructure or request related. I use a StructureMap nested container per request. I want all dependencies for an individual request to be managed by that nested container. Also once the request completes I want to "clean up" the nested container (Like commit a transaction or whatever). Currently the dependency resolver offers no way to do this. Here is a better interface IMO:

public interface IDependencyResolver
{
    // Infrastructure
    object GetInfrastructureService(Type serviceType);
    IEnumerable<object> GetInfrastructureervices(Type serviceType);

    // Per-request
    object CreateActionContainer();
    object GetActionService(object container, Type serviceType);
    IEnumerable<object> GetActionServices(object container, Type serviceType);;
    IEnumerable<object> GetActionFilters(IContainer container, IEnumerable<Type> filterTypes);
    void ReleaseActionContainer(object container);
}

I'm sure its unlikely that we'll ever see anything like this from the MVC team but we can put together something on top of the current interface that gets us there:

public abstract class DependencyResolverBase<T> : IDependencyResolver, IFilterProvider where T : class
{
    private const string MetadataKey = "___DependencyResolverContainer___";
    private static readonly Type[] ActionFilterTypes = new[] { typeof(IAuthorizationFilter), typeof(IActionFilter), 
                                                               typeof(IResultFilter), typeof(IExceptionFilter) };
    private readonly Func<IDictionary> _getRequestMetadata ;

    protected DependencyResolverBase()
    {
        _getRequestMetadata = () => HttpContext.Current.Items;
    }

    protected DependencyResolverBase(Func<IDictionary> getRequestMetadata)
    {
        _getRequestMetadata = getRequestMetadata;
    }

    // Infrastructure Services
    public abstract object GetInfrastructureService(Type serviceType);
    public abstract IEnumerable<object> GetInfrastructureServices(Type serviceType); 

    // Action Services
    public abstract T CreateActionContainer();
    public abstract object GetActionService(T container, Type serviceType);
    public abstract IEnumerable<object> GetActionServices(T container, Type serviceType);
    public abstract IEnumerable<object> GetActionFilters(T container, IEnumerable<Type> filterTypes);
    public abstract void ReleaseActionContainer(T container);
 
    public void ReleaseActionContainer()
    {
        var container = CurrentContainer;
        if (container != null) ReleaseActionContainer(container);            
    }

    public void RegisterHttpApplication(HttpApplication application)
    {
        application.EndRequest += (s, e) => ReleaseActionContainer();
    }

    object IDependencyResolver.GetService(Type serviceType)
    {
        return IsInfrastructureService(serviceType) ?
            GetInfrastructureService(serviceType) : 
            GetActionService(GetActionContainer(), serviceType);
    }

    IEnumerable<object> IDependencyResolver.GetServices(Type serviceType)
    {
        return IsInfrastructureService(serviceType) ?
            GetInfrastructureServices(serviceType).
                Union(GetBuiltInInfrastructureServices(serviceType)) :
            GetActionServices(GetActionContainer(), serviceType);
    }

    IEnumerable<Filter> IFilterProvider.GetFilters(ControllerContext controllerContext, 
                                                   ActionDescriptor actionDescriptor)
    {
        return GetActionFilters(GetActionContainer(), ActionFilterTypes, 
                                controllerContext, actionDescriptor).
                    Select(x => new Filter(x, FilterScope.Global, null));
    }

    private IEnumerable<object> GetBuiltInInfrastructureServices(Type serviceType)
    {
        return serviceType == typeof(IFilterProvider) ? 
                        new List<object> {this}:
                        Enumerable.Empty<object>();
    }

    private T GetActionContainer()
    {
        var container = CurrentContainer;
        if (container == null) CurrentContainer = container = CreateActionContainer();
        return container;
    }

    private T CurrentContainer
    {
        get { return (T)_getRequestMetadata()[MetadataKey]; }
        set { _getRequestMetadata()[MetadataKey] = value; }
    }       

    private static bool IsInfrastructureService(Type type)
    {
        return type.Namespace != null && type.Namespace.StartsWith("System.Web.Mvc");
    }
}

This class uses the template method so you can fill in the details in a derived class. Here is an example with StructureMap:

public class StructureMapDependencyResolver : DependencyResolverBase<IContainer>
{
    public override object GetInfrastructureService(Type serviceType)
    {
        return Resolve(ObjectFactory.Container, serviceType);
    }

    public override IEnumerable<object> GetInfrastructureServices(Type serviceType)
    {
        return ObjectFactory.Container.GetAllInstances(serviceType).Cast<object>();
    }

    public override IContainer CreateActionContainer()
    {
        return ObjectFactory.Container.GetNestedContainer();
    }

    public override object GetActionService(IContainer container, Type serviceType)
    {
        return Resolve(container, serviceType);
    }

    public override IEnumerable<object> GetActionServices(IContainer container, Type serviceType)
    {
        return container.GetAllInstances(serviceType).Cast<object>();
    }

    public override IEnumerable<object> GetActionFilters(IContainer container, IEnumerable<Type> filterTypes)
    {
        return container.Model.AllInstances.
            Join(filterTypes, x => x.PluginType, y => y, (x, y) => x).
            Select(x => x.ConcreteType).
            Distinct().
            Select(container.GetInstance);
    }

    public override void ReleaseActionContainer(IContainer container)
    {
        container.Dispose();
    }

    private static object Resolve(IContainer container, Type serviceType)
    {
        return (serviceType.IsAbstract || serviceType.IsInterface) ?
            container.TryGetInstance(serviceType) :
            container.GetInstance(serviceType);
    }
}

Registration is pretty straight forward. The DependencyResolverBase class needs to subscribe to the request end event in order to release the container. This event needs to be subscribed to for each http application (And there can be many) thus the override of Init(). The dependency resolver automatically registers itself as a filter provider so no need to manually register it.

public class MvcApplication : HttpApplication
{
    /...

    protected void Application_Start()
    {
        ObjectFactory.Configure(x =>
                                    {
                                        x.For<IDatabase>().Use<MongoDB>();
                                        x.For<IActionFilter>().Use<SomeFilter>();
                                        x.For<IResultFilter>().Use<SomeFilter>();
                                    });

        var dependencyProvider = new StructureMapDependencyResolver();
        DependencyResolver.SetResolver(dependencyProvider);
        // ...
    }

    public override void Init()
    {
        ((StructureMapDependencyResolver)DependencyResolver.Current).RegisterHttpApplication(this);
    }
}