Andrew Stephens
Andrew Stephens

Reputation: 10193

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?

Upvotes: 2

Views: 701

Answers (1)

Ufuk Hacıoğulları
Ufuk Hacıoğulları

Reputation: 38468

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(); 
}

Upvotes: 2

Related Questions