Castle Windsor: Force resolver to use specified constructor
Asked Answered
M

2

8

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.

Mcandrew answered 28/10, 2011 at 8:11 Comment(9)
If it fits your model, you could try using the Typed Factory facility. I believe it will automatically pick the constructor that matches the parameters you pass in to the factory method. This means you'd have to have a factory and an instance of that type to pass in, tho. If you end up keeping your existing code, and if you're registering both in the same installer, and if ComponentB doesn't have dependencies, consider skipping the registration of IComponentB/IComponentA entirely.Doddering
This question and some of the comments (e.g. ISubDependencyResolver) might also be useful.Doddering
Why does it matter which .ctor gets used?Rearrange
@Krzysztof Koźmic E.g. Constructor ComponentC(IComponentA) creates IComponentB by itself and don't use IComponentB from container. ComponentC(IComponentB) creates IComponentA by itself and don't use IComponentA from container. As a result the behavior of ComponentC is different in these cases.Mcandrew
So let me see if I get this straight - you expose two dependencies, register them both in the container, but then want just one to be provided and manage the other manually? Then why expose it as a dependency at all?Rearrange
@Krzysztof Koźmic. e.g. when ComponentC should be registered in container twice (with different IDs): 1) using ctor ComponentC(IComponentA) 2) using ctor ComponentC(IComponentB)Mcandrew
I'd say you might have two responsibilities in your class, perhaps you should split them?Rearrange
@Krzysztof Koźmic Yes, that's one more way to solve it: Create new component that inherits ComponentC then this component will use required ctor for ComponentC. But I'd like to have syntax similar to Register(Component.For<IComponentC>().ImplementedBy<ComponentB>() .UsingConstructor<IComponentB>()) :) . Is there something similar in the Castle Windsor?Mcandrew
@AlexanderStepaniuk nope, nothing like that is provided, since it goes to unnecessary level of details.Rearrange
R
6

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.

Rearrange answered 3/9, 2012 at 21:32 Comment(2)
Good explanation. I'd agree with that. Thanks.Mcandrew
Your first bullet point took me to single responsibility principle of SOLID paradigm. Very impressive answer indeed! Your answer made my day and solved a mysterious windsor castle behavior my team-mate was observing. We are working on an ASP.net MVC project which has controllers with overloaded constructors and I was puzzled that how the heck the DI container is always calling the constructor with highest number of parameters.Erle
N
1

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.

Naught answered 17/11, 2015 at 14:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.