Thursday, February 18, 2010

10 Advanced Windsor Tricks – 12. The NamingPartsSubsystem

Here’s part twelve of (slightly more than) 10 Advanced Windsor Tricks.

I’ve covered this before on this blog, but it was a while back and it’s such a neat trick that I don’t feel too bad about repeating myself. The NamingPartsSubsystem allows you to resolve components based on attributes. The attributes themselves are defined as part of the component’s name, so for example, we could have a component with a name like “thing:colour=red,version=2” and then resolve it by asking for a component with id “thing:colour=red”.

The name should match the following syntax:

<name>:<key 1>=<value 1>[,<key n>=<value n>]

Let’s say we define an interface and a number of classes that implement it:

private interface IThingy{}
private class Thing1 : IThingy {}
private class Thing2 : IThingy {}
private class Thing3 : IThingy {}
private class Thing4 : IThingy {}

Now we register them with various variations of the attributes ‘colour’ and ‘version’:

var container = new WindsorContainer();
container.Kernel.AddSubSystem(SubSystemConstants.NamingKey, new NamingPartsSubSystem());

container.Register(
        Component.For<IThingy>().ImplementedBy<Thing1>().Named("thing:colour=red,version=1"),
        Component.For<IThingy>().ImplementedBy<Thing2>().Named("thing:colour=blue,version=1"),
        Component.For<IThingy>().ImplementedBy<Thing3>().Named("thing:colour=red,version=2"),
        Component.For<IThingy>().ImplementedBy<Thing4>().Named("thing:colour=blue,version=2")
    );

Note the way we register the NamingPartsSubsystem.

OK, so now we have four different implementations of IThingy some red some blue, some version 1 and some version 2. Now if we just ask for a ‘thing’ we will get Thing1 since that was the first component named ‘thing:…’ that was registered:

var defaultThing = container.Resolve("thing");
Console.WriteLine("defaultThing = {0}", defaultThing.GetType().Name);

Outputs:
 
defaultThing = Thing1

If we ask for a red thing, we will still get Thing1 since that was the first red thing that was registered:

var redThing = container.Resolve("thing:colour=red");
Console.WriteLine("redThing.GetType().Name = {0}", redThing.GetType().Name);

Outputs:

redThing.GetType().Name = Thing1

If we ask for a blue thing we get Thing2 since that was the first blue thing that was registered:

var blueThing = container.Resolve("thing:colour=blue");
Console.Out.WriteLine("blueThing.GetType().Name = {0}", blueThing.GetType().Name);

Outputs:

blueThing.GetType().Name = Thing2

If we ask for a version 2 thing and we don’t care about the colour, we get Thing3, the first version 2 thing registered:

var version2 = container.Resolve("thing:version=2");
Console.Out.WriteLine("version2.GetType().Name = {0}", version2.GetType().Name);

Outputs:

version2.GetType().Name = Thing3

Finally if we ask for a version 2 blue thing, we get Thing4 since it’s the only thing that is both blue and version 4:

var blueThingVersion2 = container.Resolve("thing:colour=blue,version=2");
Console.Out.WriteLine("blueThingVersion2.GetType().Name = {0}", blueThingVersion2.GetType().Name);

Outputs:

blueThingVersion2.GetType().Name = Thing4

This is a very useful trick if you want to resolve components based on attributes that you configure them with. We’ll see a very neat use of this when we discuss IHandlerSelector next.

No comments: