Mark Cassidy
Mark Cassidy

Reputation: 5860

Run-time type determination using .AsFactory()

After reading through questions here; and attempting to follow the instructions provided in http://docs.castleproject.org/(X(1)S(0w0clnf021ee0445lol02255))/Default.aspx?Page=Typed-Factory-Facility-interface-based-factories&NS=Windsor&AspxAutoDetectCookieSupport=1, I must be missing a link somewhere.

To summarise what I am attempting to do:

I have an interface for a factory:

public interface IFacilityTypeDescriptionBuilderFactory
{
    IFacilityTypeDescriptionBuilder CreateFacilityTypeDescriptionBuilder(Facility facility);
}

The purpose of this factory being; based on specific business rules and property values of the provided Facility, a different type is going to be returned. It will be of type IFacilityTypeDescriptionBuilder obviously, but the concrete type will be determined by the factory (but not instantiated by it).

The most straight forward implementation for this would be, to register the IFacilityTypeDescriptionBuilderFactory service, do .ImplementedBy and then just let the factory instantiate the returning objects. But in doing so, I would be forced to use/reference/have a dependency on the service locator, so I'd rather not.

According to the post above; I should be able to achieve what I am trying to do, by utilising the ITypedFactoryComponentSelector - and instruct Windsor on what to do. A concrete implementation of IFacilityTypeDescriptionBuilderFactory should not be required.

Here's what I currently do:

        args.Container.AddFacility<TypedFactoryFacility>();

        args.Container.Register(
            Component.For<IFacilityTypeDescriptionBuilderFactory>()
                     .AsFactory()
            );

        args.Container.Register(
            Component.For<ITypedFactoryComponentSelector>()
                     .ImplementedBy<FacilityTypeDescriptionBuilderComponentSelector>()
            );

And then my ComponentSelector:

public class FacilityTypeDescriptionBuilderComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override System.Type GetComponentType(System.Reflection.MethodInfo method, object[] arguments)
    {
        if (method.Name == "CreateFacilityTypeDescriptionBuilder" && arguments.Length == 1 && arguments[0] is Facility)
        {
            var facility = arguments[0] as Facility;
            switch (facility.FacilityTypeId)
            {
                case 1:
                    return typeof (RestaurantFacilityTypeDescriptionBuilder);
                default:
                    return typeof (EmptyFacilityTypeDescriptionBuilder);
            }
        }

        return base.GetComponentType(method, arguments);
    }
}

With all of this in place, I now attempt to assert that everything is wired up correctly. And it's not; in this example "b" ends up being NULL.

        var f = new Facility();
        f.FacilityTypeId = 1;
        var b = args.Container.Resolve<IFacilityTypeDescriptionBuilderFactory>().CreateFacilityTypeDescriptionBuilder(f);
        Assert.IsNotNull(b as RestaurantFacilityTypeDescriptionBuilder, "b");

From what I can determine, the ComponentSelector is never called - or at least not in the way I expect it to. The error I get is this:

No component for supporting the service XYZ.Common.Builders.IFacilityTypeDescriptionBuilder was found

So basically Windsor realises that it has to return IFacilityTypeDescriptionBuilder from the .CreateFacilityTypeDescriptionBuilder(f) method call - it just doesn't know how.

Can anyone point out to me, what I'm missing here?

Upvotes: 2

Views: 408

Answers (1)

Krzysztof Kozmic
Krzysztof Kozmic

Reputation: 27374

you need

.AsFactory(f => f.SelectedWith<FacilityTypeDescriptionBuilderComponentSelector>())

Upvotes: 2

Related Questions