Support for Arrays in Constructors using Castle Windsor

As I mentioned in my post Aspect Oriented Timing in C# with Castle Windsor, we are using Castle Windsor as our Inversion of Container on our current project.

In the process of adding a couple of new features, there were places where the new code had to fit in to existing places where there were a longer (more than simply just a single if/else) set of conditionals. To address the changes that I needed to make, I added another conditional to go with the existing ones. This change was in the mood of make it work, knowing I would come back after it worked to make it right, and that this change would allow me to confirm the smell I was “sure” I was smelling.

One I had it working, I came back to make it right. The route I wanted to go was to emulate the refactoring Replace Conditional with Polymorphism by creating a “handler” abstraction, and pulling the logic for each of the conditionals into a separate implementation of the handler.

My new interface was defined as:

public interface INewHandler
{
  bool CanHandle(ISomeObject obj);
  void Handle(ISomeObject obj);
}

I then moved out the logic in each of the conditionals, as well as the conditionals themselves into new handlers. The conditionals were moved into the CanHandle method, and the body of the conditional was moved to the HandleMethod of the implementation.

With these defined, it became just a matter of using a LINQ query to find either the first, or all items that could handle the object, and then just call the Handle method on the ones that matched the filter.

injectedHandlers.
    Where(h => h.CanHandle(someObj)).
    ForEach(h => h.Handle(someObj));

After having this, all we need now is the ability to have all of the implementations injected into the constructor. This gets us to the title of this post, about having arrays injected into constructors. First we have to configure the container to add a new ArrayResolver as a subresolver.

container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));

After this, I want to pick up all implementations of the INewHandler in all assemblies.

container.Register(
    AllTypes.FromAssemblyInDirectory(new AssemblyFilter("bin")).
    BasedOn>INewHandler>().
    WithService.Base().
    Configure(c => c.LifeStyle.PerWebRequest)
);

After adding these two to the registration of Castle Windsor, I now get all implementations injected into my constructor when I declare a constructor parameter that is a type of INewHandler[].

Hope someone else can find this helpful.

–Proctor