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:
AllTypes
.Of<IController>()
.FromAssembly(Assembly.GetExecutingAssembly())
);
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 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 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:
AllTypes
.Pick()
.FromAssembly(Assembly.GetExecutingAssembly())
.WithService
.FirstInterfaceOnType()
);
[...] 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 [...]
By: ASP.NET MVC Archived Blog Posts, Page 1 on June 15, 2009
at 2:58 am
Hello, can you please post some more information on this topic? I would like to read more.
By: GarykPatton on June 16, 2009
at 3:07 am
Thank you so much, I’ve been experiencing this issue too, and this post saved me much time and hair-pulling!
By: Ian Nelson on September 8, 2009
at 8:01 am
Great tip! Thank you!
By: Andreas N on January 25, 2010
at 11:50 am