Search code examples
castle-windsor

Any alternative to injecting Castle Windsor typed factories?


Most of my components are registered using the code-based (fluent) approach, but there is one particular component that I need to resolve differently at runtime. This is the interface and a couple of concrete implementations:-

public interface ICommsService ...

public class SerialCommsService : ICommsService ...

public class TcpCommsService : ICommsService ...

Some of our users will need the serial service while others will need the TCP service. My current solution (which works btw) is to use a typed factory and a custom component selector - the latter reads an app.config setting to determine which implementation the typed factory will resolve and return.

First the typed factory (nothing special about this):-

public interface ICommsServiceFactory
{
    ICommsService Create();
    void Release(ICommsService component);
}

Next, the custom component selector, which reads the fully-qualified type name from app.config (e.g. "MyApp.SomeNamespace.TcpCommsService"):-

public class CommsFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return ConfigurationManager.AppSettings["commsServiceType"];
    }
}

Then the registration stuff:-

var container = new WindsorContainer();

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ITypedFactoryComponentSelector>()
         .ImplementedBy<CommsFactoryComponentSelector>());
container.Register(Component.For<ICommsFactory>()
         .AsFactory(o => o.SelectedWith<CommsFactoryComponentSelector>()));

container.Register(Component.For<ICommsService>()
         .ImplementedBy<SerialCommsService>().LifeStyle.Singleton);
container.Register(Component.For<ICommsService>()
         .ImplementedBy<TcpCommsService>().LifeStyle.Singleton);

Finally, an example class with a dependency on ICommsService:-

public class Test
{
    public Test(ICommsFactory commsFactory)
    {
        var commsService = commsFactory.Create();
        ...
    }
}

As already mentioned, the above solution does work, but I don't like having to inject the factory. It would be more intuitive if I could just inject an ICommsService, and let something somewhere figure out which implementation to resolve and inject - similar to what I'm doing now but earlier in Windsor's "resolving pipeline". Is something like that possible?


Solution

  • You can use UsingFactoryMethod here:

    container.Register(Component.For<ICommsService>().UsingFactoryMethod(kernel => kernel.Resolve<ICommsServiceFactory>().Create()));
    

    You can inject ICommsService to any class now. ICommsServiceFactory can be a simple interface now:

    interface ICommsServiceFactory
    {
        ICommsService Create(); 
    }