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()
);