logo

Web API Dependency Resolver with Castle Windsor's Scoped Lifetimes

Update: Mark Seemann has provided a solution without using the IDependencyResolver interface.

This post is the result of a very good suggestion in the comments section of the previous post.

The WindsorDependencyScope from the previous post has been modified to use the Scoped lifetime available in Castle Windsor 3.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Dependencies;
using Castle.MicroKernel.Lifestyle;
using Castle.Windsor;

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer container;
    private readonly IDisposable scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        this.container = container;
        this.scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return this.container.Kernel.HasComponent(t) ? this.container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return this.container.ResolveAll(t).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        this.scope.Dispose();
    }
}

The BeginScope is an extension method for the IWindsorContainer type. It returns by default an instance of a CallContextLifetimeScope type. It uses the Call Context so it can be associated with thread pool threads and manually created threads within a single AppDomain (it does not use the Logical Call Context).

On each request the Web API calls the GetDependencyScope extension method of the HttpRequestMessage type, which, in return, calls it’s own BeginScope method to start a new resolution scope. Using our own implementation of the IDependencyResolver interface we always return a new instance of the WindsorDependencyScope type.

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    // 'using' Directives and other type members removed for brevity.

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(this.container);
    }
}

Since we use the Scoped lifetime we need to define it also in the registration code. Then, we will always have at most one instance of each requested type per resolution scope (that is, a request).

internal sealed class WebWindsorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes
            .FromAssemblyContaining<ValuesController>()
            .BasedOn<IHttpController>()
            .LifestyleScoped());
    }
}

The source code can be found here.