logo

Web API Dependency Resolver with Castle Windsor

The code in this post requires the ASP.NET MVC 4 Beta. If you use the RC version use the code from this post.

Update: Mark Seemann has provided a great post on this subject.

In this post I will discuss a possible solution for having Castle Windsor resolving types for both MVC controllers and Web API controllers. Since the former is well known I will focus mostly on the Web API part.

A team building a mobile web application uses the ASP.NET MVC stack combined with another web services framework. With the release of the Beta version of ASP.NET MVC 4 they decided to give Web API a try (side by side with MVC controllers) and see if it fits their needs.

The Controller-derived types are used only for rendering the views (the code is written in JavaScript) while the ApiController-derived types are used for returning  data to the client.

The DependencyResolver class provides a method called SetResolver which acts as a registration point for resolving dependencies.

Be careful as there are more than one DependencyResolver types. One defined in System.Web.Mvc namespace and one defined in System.Web.Http namespace. We need the latter here.

Once we define the delegates and set a breakpoint we can see what types the framework requests.

At first an instance of the IHttpControllerFactory type is requested:

IHttpControllerFactory type is requested

Followed by a request for an instance of the ILogger type and so on.

ILogger type is requested

The first thing we want to do is to create a type implementing the IHttpControllerFactory interface.

using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using Castle.MicroKernel;

internal class WindsorHttpControllerFactory : IHttpControllerFactory
{
    private readonly HttpConfiguration configuration;
    private readonly IKernel kernel;

    public WindsorHttpControllerFactory(
        HttpConfiguration configuration,
        IKernel kernel)
    {
        this.configuration = configuration;
        this.kernel = kernel;
    }

    public IHttpController CreateController(
        HttpControllerContext controllerContext,
        string controllerName)
    {
        var controller = this.kernel.Resolve<IHttpController>(controllerName);

        controllerContext.Controller = controller;
        controllerContext.ControllerDescriptor = new HttpControllerDescriptor(
            this.configuration,
            controllerName,
            controller.GetType());

        return controllerContext.Controller;
    }

    public void ReleaseController(IHttpController controller)
    {
        this.kernel.ReleaseComponent(controller);
    }
}

Note that inside the WindsorHttpControllerFactory class the CreateController method  takes a string for the name of the controller. That means we need to use the Windsor's Named method to set a name for each controller registration. (We can also trim the "Controller" part from the name and also pluralize the remaining part.)

foreach (Type controller in typeof(OrderController).Assembly.GetTypes()
    .Where(type => typeof(IHttpController).IsAssignableFrom(type)))
{
    // https://github.com/srkirkland/Inflector/
    string name = Inflector.Pluralize(
        controller.Name.Replace("Controller", ""));

    container.Register(Component
        .For(controller)
        .Named(name)
        .LifestylePerWebRequest());
}

Let's also create a NullLogger implementing the ILogger interface.

using System;
using System.Diagnostics;
using System.Web.Http.Common;

internal class NullLogger : ILogger
{
    public void Log(string category, TraceLevel level,
        Func<string> messageCallback)
    {
    }

    public void LogException(string category, TraceLevel level,
        Exception exception)
    {
    }
}

For all the other instances that the framework requests there are default implementations in the System.Web.* assemblies and we can now create a Windsor Installer to encapsulate the registration logic.

using System;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
using System.Web.Http.Common;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Metadata;
using System.Web.Http.Metadata.Providers;
using System.Web.Http.ModelBinding;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

internal class WebApiInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IHttpControllerFactory>()
               .ImplementedBy<WindsorHttpControllerFactory>()
               .LifestyleSingleton(),

            Component.For<ILogger>()
               .ImplementedBy<NullLogger>()
               .LifestyleSingleton(),

            Component.For<IFormatterSelector>()
               .ImplementedBy<FormatterSelector>()
               .LifestyleSingleton(),

            Component.For<IHttpControllerActivator>()
               .ImplementedBy<DefaultHttpControllerActivator>()
               .LifestyleTransient(),

            Component.For<IHttpActionSelector>()
               .ImplementedBy<ApiControllerActionSelector>()
               .LifestyleTransient(),

            Component.For<IActionValueBinder>()
               .ImplementedBy<DefaultActionValueBinder>()
               .LifestyleTransient(),

            Component.For<IHttpActionInvoker>()
               .ImplementedBy<ApiControllerActionInvoker>()
               .LifestyleTransient(),

            Component.For<ModelMetadataProvider>()
               .ImplementedBy<CachedDataAnnotationsModelMetadataProvider>()
               .LifestyleTransient(),

            Component.For<HttpConfiguration>()
               .Instance(GlobalConfiguration.Configuration));
    }
}

In the Application_Start method we add the installer and set the delegates for the SetResolver method. That way when the framework requests an IHttpControllerFactory instance, Windsor will supply the one we created earlier.

this.container = new WindsorContainer()
    .Install(new WebApiInstaller());

GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
    serviceType => container.Resolve(serviceType),
    serviceType => container.ResolveAll(serviceType).Cast<object>());

In order to have Windsor resolve regular controllers (side by side) we can create and add another installer as well as an implementation of the IControllerFactory interface.

this.container = new WindsorContainer()
    .Install(new WebMvcInstaller())
    .Install(new WebApiInstaller());

GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
    serviceType => container.Resolve(serviceType),
    serviceType => container.ResolveAll(serviceType).Cast<object>());

ControllerBuilder.Current.SetControllerFactory(
    new WindsorControllerFactory(this.container));

Finally, a gist with all the source code can be found here.

References: