Monday, March 09, 2009

Castle Windsor: Registering and resolving arrays of dependencies using the fluent registration API

The Castle Windsor fluent registration API is a powerful and beautiful thing, but it's very new and the documentation is still evolving. Mostly I find the best thing is just to read the code, especially the unit tests. Today I wanted to register an array of components that all implement the same interface, and then resolve them in a particular order for another component that expects an array of that interface in its constructor.

Here's how it's done. First lets define an interface and some types that implement it:

public interface IMyInterface { }
public class MyFirstThing : IMyInterface {}
public class MySecondThing : IMyInterface {}
public class MyThirdThing : IMyInterface {}

Then we have a component that has a dependency on IMyInterface:

public class HasArrayDependency
{
    private readonly IMyInterface[] myInterfaces;
    public HasArrayDependency(IMyInterface[] myInterfaces)
    {
        this.myInterfaces = myInterfaces;
    }
    public IMyInterface[] MyInterfaces
    {
        get { return myInterfaces; }
    }
}

Here's a test showing the registration:

[Test]
public void Demonstrate_fluent_registration_of_arrays()
{
    var container = new WindsorContainer()
        .Register(
            Component.For<HasArrayDependency>()
                .ServiceOverrides(
                    ServiceOverride.ForKey("myInterfaces").Eq(
                        "MyFirstThing",
                        "MySecondThing",
                        "MyThirdThing"
                    )
                ),
            AllTypes
                .FromAssembly(Assembly.GetExecutingAssembly())
                .BasedOn<IMyInterface>()
                    .WithService.FromInterface(typeof(IMyInterface))
                    .Configure(c => c.Named(c.Implementation.Name))
        );
    var hasArrayDependency = container.Resolve<HasArrayDependency>();
    Assert.That(hasArrayDependency.MyInterfaces[0].GetType().Name, Is.EqualTo("MyFirstThing"));
    Assert.That(hasArrayDependency.MyInterfaces[1].GetType().Name, Is.EqualTo("MySecondThing"));
    Assert.That(hasArrayDependency.MyInterfaces[2].GetType().Name, Is.EqualTo("MyThirdThing"));
}

There are a couple of things we need to do. If we simply register components like this:

var container = new WindsorContainer()
    .Register(
        AllTypes
            .FromAssembly(Assembly.GetExecutingAssembly())
    );

By default they will be named with their full type name rather than just the class name, so we have to use the Configure method with the lambda expression to change the component name to the class name.

Next we use the ServiceOverrides method to override the default dependency resolution. Here we are saying that for the constructor parameter named 'myInterfaces' we are going to supply the components named 'MyFirstThing', 'MySecondThing', and 'MyThirdThing'. Castle Windsor doesn't provide array parameters with any service of the array type by default, if you want that behaviour you need to use a custom sub dependency resolver as described here.

3 comments:

preet sangha said...
This comment has been removed by the author.
preet sangha said...

(whoops - trying again)

Thank you! I'm just about to investigate moving our factory implementation to something more test friendly and this has popped up just in time.

Mike Hadlow said...

Thanks Preet, Glad it was useful :)