Testing Castle Windsor Mappings Part Deux

In my original post on testing Windsor Container mappings, I posted a spec to run whenever you are using Castle Windsor in your project. It basically ran through your configuration file and ensured all the mappings worked. This was meant to be a safety net to catch a rename or namespace move in the domain (which wouldn't update the configuration file).

It worked pretty good and has helped us catch silly errors but we were getting pain with mappings on classes like this:

public partial class FinderGrid<T> : UserControl, IFinderGrid<T> where T : Entity
{
}

Generics were a funny thing so you get an error like this:

System.ArgumentException: GenericArguments[0], 'T',
on 'UserInterface.FinderGrid`1[T]' violates the constraint of type 'T'.
---> System.TypeLoadException: GenericArguments[0], 'T',
on 'UserInterface.FinderGrid`1[T]' violates the constraint of type parameter 'T'.
   at System.RuntimeTypeHandle._Instantiate(RuntimeTypeHandle[] inst)
   at System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle[] inst)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   --- End of inner exception stack trace ---
   at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, Type[] genericArguments, Exception e)
   at System.RuntimeType.MakeGenericType(Type[] instantiation)
   at Castle.MicroKernel.Handlers.DefaultGenericHandler.Resolve(CreationContext context) 

We put an exception handler around the test, but it was butt ugly. After some posting on the Castle mailing list and discussion with a few people I came up with a way to handle any generic mapping you have. Here's the updated spec that will deal with (hopefully) any mapping:

[TestFixture]
public class When_starting_the_application : Spec
{
    [Test]
    public void Verify_Windsor_Container_mapping_configuration_is_correct()
    {
        IWindsorContainer container = new WindsorContainer("castle.xml");
        foreach (IHandler handler in container.Kernel.GetAssignableHandlers(typeof (object)))
        {
            if (handler is DefaultGenericHandler)
            {
                Type[] genericArguments = handler
                    .ComponentModel
                    .Service
                    .GetGenericArguments();
 
                foreach (Type genericArgument in genericArguments)
                {
                    Type[] genericParameterConstraints =
                        genericArgument.GetGenericParameterConstraints();
                    foreach (Type genericParameterConstraint in genericParameterConstraints)
                    {
                        container.Resolve(
                            handler
                                .ComponentModel
                                .Service
                                .MakeGenericType(genericParameterConstraint));
                    }
                }
            }
            else
            {
                container.Resolve(handler.ComponentModel.Service);
            }
        }
    }
}

This will handle any mapping with constraints and the name isn't tied to your test (originally I found one that worked but I had to specify the generic type when trying to set the constraint). This is a 2.0 solution and not as pretty as it could be with LINQ and 3.5 but it works.

Hope this helps!

P.S. Here's a silly creepy version for those that dig anonymous delegates to the extreme. Lamdbas would just make most of this syntax sugar go away:

[Test]
public void Verify_Windsor_Container_mapping_configuration_is_correct()
{
    IWindsorContainer container = new WindsorContainer("castle.xml");
    foreach (IHandler handler in container.Kernel.GetAssignableHandlers(typeof (object)))
    {
        if (handler is DefaultGenericHandler)
        {
            new List<Type>(handler
                               .ComponentModel
                               .Service
                               .GetGenericArguments())
                .ForEach(
                delegate(Type argument)
                    {
                        new List<Type>(argument.GetGenericParameterConstraints())
                            .ForEach(
                            delegate(Type constraint)
                                {
                                    container
                                        .Resolve(
                                        handler
                                            .ComponentModel
                                            .Service
                                            .MakeGenericType(constraint));
                                }
                            );
                    }
                );
        }
        else
        {
            container.Resolve(handler.ComponentModel.Service);
        }
    }

1 Comment

  • What is the required mapping to avoid the ArgumentException you posted about above? I have a seemingly innocuous mapping that used to work until I upgraded Castle but now gives me the error:


Comments have been disabled for this content.