Monday, June 25, 2012

Update: Unity with ASP.NET Web API 4 RC

Lately the Release Candidate of ASP.NET MVC 4 was released together with an updated version of the ASP.NET Web API. This blog post is an update of the previous post about a custom controller factory with Unity in Web API 4 Beta.

Changes from ASP.NET MVC 4 Beta to RC
“Independently control IHttpController selection and activation: Implement the IHttpControllerSelector to control IHttpController selection. Implement IHttpControllerActivator to control IHttpController activation. The IHttpControllerFactory abstraction has been removed.” – This is the official statement from the ASP.NET MVC site. That means that you no longer may use the IHttpControllerFactory. Instead you get fine grained control over controller selection and activation by using to separate interfaces.
The custom controller factory
For our controller factory we need to implement the IHttpControllerActivator. The interface has just a create method, used to create a certain controller:

public class UnityControllerFactory : IHttpControllerActivator
{
    private readonly IUnityContainer container;
    private readonly DefaultHttpControllerActivator defaultActivator; 

    public UnityControllerFactory(IUnityContainer container)
    {
        this.container = container;
        defaultActivator = new DefaultHttpControllerActivator();
    } 

    public IHttpController Create(
        System.Net.Http.HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        if(container.IsRegistered(controllerType))
        {
            return container.Resolve(controllerType) as IHttpController;
        }

        return defaultActivator.Create(request,
controllerDescriptor, controllerType);
    }
}

As you can see there’s a reference to the Unity container. Inside the Create method we first check, if the controller was registered with Unity or not. If it is not registered we will use a default activator to create the controller, otherwise it will be resolved using Unity.
Initializing the controller factory
The custom Http controller factory needs to be registered with the Web API pipeline in order to use it for controller creation. The HttpConfiguration in ASP.NET Web API 4 RC does not contain a service resolver any more. Instead it contains a collection of services. The collection is a specialized one and is of type DefaultServices. This collection provides methods to get services or manipulate the collection. For example it has a replace method that can be used to replace a registered type in the collection with an own implementation. We can use this method to replace the standard controller activator with our own:
var container = new UnityContainer();
container.RegisterType(typeof (Controllers.CustomerController));
var factory = new UnityControllerFactory(container); 

GlobalConfiguration.Configuration.Services.Replace(
                typeof (IHttpControllerActivator), factory);

That’s it. After registering your own factory this way it will be used for all requests to the Web API. There’s one disadvantage using this solution. When you create controllers with the Unity container you lose caching mechanisms that are built in the default Http controller activator. That means no controller objects are reused. You get a new instance on each API request. This disadvantage may be overcome by either configuring Unity to use Singleton objects or by implementing own caching machanisms.

ASP.NET Web API 4 Beta – Custom Unity Http Controller Factory

The ASP.NET Web API provides support for Restful services that make use of JSON and the concepts of ASP.NET MVC. Instead of the Controller base class in MVC the Web API uses the ApiController class. In order to use a dependency injection container like Unity for controller creation you need to create your own controller factory. This blog post shows how to build a custom Http controller factory together with the Unity application block.
A custom controller factory
Each controller factory in ASP.NET Web API must implement the IHttpControllerFactory interface. This again is different from MVC where you have to implement IControllerFactory. The IHttpControllerFactory provides just two methods – CreateController and ReleaseController. I think by reading the names it’s obvious what both methods should do. Together with Unity we will use these methods to create and release controllers with the help of the Unity container:
public class UnityControllerFactory : IHttpControllerFactory
{
    private readonly IUnityContainer container;
    private readonly IHttpControllerFactory defaultFactory;
    private readonly HttpConfiguration configuration;
    public UnityControllerFactory(IUnityContainer container,
HttpConfiguration configuration)
    {
        this.configuration = configuration;
        this.container = container;
        this.defaultFactory = new DefaultHttpControllerFactory(configuration);
    } 

    public IHttpController CreateController(
        HttpControllerContext controllerContext, string controllerName)
    {
        if (container.IsRegistered<IHttpController>(controllerName))
        {
            var controller = container.Resolve<IHttpController>(controllerName);
            controllerContext.ControllerDescriptor =
                new HttpControllerDescriptor(
this.configuration,
controllerName,
controller.GetType());
            controllerContext.Controller = controller;
            return controller;
        } 
        return defaultFactory.CreateController(controllerContext, controllerName);
    } 

    public void ReleaseController(IHttpController controller)
    {
        if (container.IsRegistered(controller.GetType()))
        {
            container.Teardown(controller);
        }
        else
        {
            defaultFactory.ReleaseController(controller);
        }
    }
} 
Let’s have a look at the code. We hold a reference to the Unity container in order to resolve controller dependencies. We also define a default factory in case a controller is not registered with unity. The framework provides the DefaultHttpControllerFactory which is normally used to create API controller. Another reference we need is to the current Http configuration. The configuration is needed to create a proper descriptor for the created controller.
The CreateController method tries to find the controller with the given name inside the Unity container. If the controller is not registered with Unity the default factory is used to create an instance of the controller. If the controller is registered it is resolved by Unity and a proper controller descriptor is generated. The descriptor together with the Http configuration is needed to build up a correct Http context in which the controller is executed.
The ReleaseController method again checks if the controller is registered with Unity or not and calls the appropriate release method.
Initializing the controller factory
If you have the custom Http controller factory there’s only one last step to use it. You need to instantiate the Unity container inside the global.asax and pass it to the factory. Additionally you need to set the factory as the default factory for creating API controllers. Here’s the short code snippet:
protected void Application_Start()
{
     
    var container = new UnityContainer();
    //register the customer controller with Unity
    container.RegisterType<IHttpController,
Controllers.CustomerController>("customer"); 
    //Add additional controllers here 
    var factory = new UnityControllerFactory(container,
GlobalConfiguration.Configuration);
    GlobalConfiguration.Configuration.ServiceResolver.SetService(
        typeof(IHttpControllerFactory), factory);
   
}
I think the code is more or less self-explanatory. By using the static Configuration property of the GlobalConfiguration  class you get access to the current Http configuration object, which is needed for the factory to build the controller descriptor. Pay attention to the controller name passed to the RegisterType method. This name should resemble the name of the controller used by API calls because that name is passed to the controller factory.