Posted by: Stephen Oakman | June 9, 2009

Extension methods we use for auto registration in Castle Windsor

The Castle Windsor 2.0 release contains the auto registration that was previously only available on the trunk (as opposed to the previous 1.0 RC 3 release). The two main auto registration methods we use are the following:

container.Register(
    AllTypes
    .Of<IController>()
    .FromAssembly(Assembly.GetExecutingAssembly())
    );

container.Register(
    AllTypes
    .Pick()
    .FromAssembly(Assembly.GetExecutingAssembly())
    .WithService
    .FirstInterface()
    );

The first one registers all ASP.NET MVC controllers into the container, whilst the second one register’s all other classes using the first interface as the service to register against. So if you have a class ServiceImplementation that implements IService then container.Resolve<IService>() will return an instance of ServiceImplementation.

Their is currently an issue with this second mechanism though in regards to derived classes. To help explain, consider the following:

public interface IFirstServiceOnBase 
{
    // …

public interface IFirstServiceOnDerived : IFirstServiceOnBase 
{
    // …

public class BaseService : IFirstServiceOnBase 
{
    // …

public class DerivedService : BaseService, IFirstServiceOnDerived
{
    // …
}

In this example we have BaseService which implements IFirstServiceOnBase. We then have DerivedService which inherits from BaseService and also implements IFirstServiceOnDerived.

What we want to happen is that when the previous auto registration reflects on our types we expect a call to container.Resolve<IFirstServiceOnBase>() to resolve to BaseService and for container.Resolve<IFirstServiceOnDerived>() to resolve to DerivedService.

However, we find that the second resolve on the container for IFirstServiceOnDerived actually throws an exception as it wasn’t registered.

So, why is this?

The answer is that interfaces are listed in the order of lowest base class first up to derived classes next. So the WithService.FirstInterface() call tries to register DerivedService against IFirstServiceOnBase as opposed to IFirstServiceOnDerived, and finds that this service interface has already been registered against BaseService.

Our fix for this is a new extension method:

public static class WindsorExtensions
{
    public static BasedOnDescriptor FirstInterfaceOnType(this ServiceDescriptor serviceDescriptor)
    {
        return serviceDescriptor.Select((type, baseType) =>
        {
            var interfaces  = type.GetInterfaces().Except(type.BaseType.GetInterfaces());
            if (interfaces.Count() == 0)
                return null;
            return new [] {interfaces.First()};
        });
    }
}

The FirstInterfaceOnType extension method filters out the base interfaces and will now correctly register DerivedService against IFirstServiceOnDerived as we expect.

This changes our second auto registration call to the following:

container.Register( 
    AllTypes 
    .Pick() 
    .FromAssembly(Assembly.GetExecutingAssembly()) 
    .WithService 
    .FirstInterfaceOnType() 
    );

Responses

  1. [...] to VoteExtension methods we use for auto registration in Castle Windsor (6/9/2009)Tuesday, June 09, 2009 from weblanderThe Castle Windsor 2.0 release contains the auto registration [...]

  2. Hello, can you please post some more information on this topic? I would like to read more.

  3. Thank you so much, I’ve been experiencing this issue too, and this post saved me much time and hair-pulling!

  4. Great tip! Thank you!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Categories

Follow

Get every new post delivered to your Inbox.