Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Windsor: Force resolver to use specified constructor

Here is the example:

interface IComponentA {};

class ComponentA : IComponentA { };

interface IComponentB { };

class ComponentB : IComponentB { };

interface IComponentC { };

class ComponentC : IComponentC
{
    public ComponentC(IComponentA a)
    {
        Console.WriteLine("Constructor A"); 
    }

    public ComponentC(IComponentB b) 
    {
        Console.WriteLine("Constructor B");
    }
};

All these components are registered in Castle Windsor container.

But class ComponentC has 2 overloaded constructors. Any of them can be used when ComponentC is being activated.

I need ComponentC(IComponentB b) constructor to be used.

For a moment I'm using UsingFactoryMethod() method to resolve that:

container
    .Register(Component
        .For<IComponentA>()
        .ImplementedBy<ComponentA>())
    .Register(Component
        .For<IComponentB>()
        .ImplementedBy<ComponentB>())
    .Register(Component
        .For<IComponentC>()
        .UsingFactoryMethod(() => new ComponentC(
            container.Resolve<IComponentB>())));

It works, but probably Castle Windsor provides some better way to do that?

Any help is really appreciated.

Thanks.

like image 360
Alexander Stepaniuk Avatar asked Oct 28 '11 08:10

Alexander Stepaniuk


2 Answers

Windsor doesn't provide support for this scenario, because it breaks one of the unwritten assumptions it (and most containers) operates based on: "all constructors are created equal".

What that means, is that regardless of which constructor it choses there should be no functional differences in the behaviour of the component. All things being equal the more dependencies a component has the more capabilities it has, that's why Windsor will tend to pick greedier constructors first, but in case like yours I'd say either of two things are happening:

  • your component may actually be two components disguised as one. In that case you probably will want to split it.
  • your component actually does operate with both dependencies it has, and therefore it should have a single constructor that takes them both.

Another scenario I've seen is something like that:

public class Foo
{
   public Foo(ISession session){/*code*/}
   public Foo(ISessionFactory factory):this(factory.OpenSession()){}
}

While this might seem like a clever idea at first, at best it's superfluous, confusing and unnecessary. If your case looks like this one, I'd just remove the second constructor.

like image 123
Krzysztof Kozmic Avatar answered Sep 28 '22 20:09

Krzysztof Kozmic


Umm, it's awful but there is one way (I've had to do this with Linq2Sql DataContext objects in the past). You create a decorator class and register that instead.

Let's say you have this interface:

public interface IService 
{
    void DoSomething();
}

And you have an implementation as follows:

public class Service : IService
{
    private readonly ILogger _logger;

    public Service(ILogger logger)
        : this(logger, SomeDefaultListOfThings())
    {
    }

    // Let's say Windsor is calling this ctor for some reason (ArrayResolver?)
    public Service(ILogger logger, IEnumerable<object> emptyArrayFromWindsor)
    {
        _logger = logger;
        PutTheItemsSomewhere(emptyArrayFromWindsor);
    }

    public void DoSomething()
    {
        // Something that relies on the list of items...
    }
}

But as the example suggests, for some reason Windsor is calling the wrong ctor and you can't convince it otherwise (as Krzysztof correctly points out). Then you could create the decorator class as follows, which only has one constructor and thus removes the ambiguity:

public class SpecificCtorServiceDecorator : IService
{
    private readonly IService _decorated;

    public SpecificCtorServiceDecorator(ILogger logger)
    {
        _decorated = new Service(logger);
    }

    public void DoSomething()
    {
        _decorated.DoSomething();
    }
}

You'd then register that class instead:

container.Register(
    Component.For<IService>()
             .ImplementedBy<SpecificCtorServiceDecorator>());

Of course better would be to not have this weird default-values-in-other-constructors thing going on (search for "Poor-man's Dependency Injection"), but in the case where you're not in control of the class you actually want (like I was in Linq2Sql, or if it would be a breaking change to an API) then this might get you out of trouble.

like image 27
Neil Barnwell Avatar answered Sep 28 '22 20:09

Neil Barnwell