Castle Windsor with Multiple Constructors
Asked Answered
V

2

6

I am currently undertaking a conversion, from to the use of Ninject, to the current release of Castle Windsor for a simple C# .NET application.

For the most part, the conversion has gone well and the implementation of the containers has executed flawlessly. I am however having a small issue with my repository objects.

I have a user repository object that is coded in the following fashion:

public class UserRepository : IUserRepository {
    public UserRepository(IObjectContext objectContext)  {
        // Check that the supplied arguments are valid.
        Validate.Arguments.IsNotNull(objectContext, "objectContext");

        // Initialize the local fields.
        ObjectContext = objectContext;
    }

    public UserRepository(IObjectContextFactory factory) 
        : this(factory.CreateObjectContext()) { 
    }

    // -----------------------------------------------
    // Insert methods and properties...
    // -----------------------------------------------
}

To correspond to this code, I have setup the following entries in my application's configuration file:

<castle>
    <components>
        <component id="objectContextFactory" lifestyle="custom"
                   customLifestyleType="Common.Infrastructure.PerWebRequestLifestyleManager, Common.Castle"
                   service="Project.DAL.Context.IObjectContextFactory, Project.DAL.LINQ"
                   type="project.DAL.Context.ObjectContextFactory, Project.DAL.LINQ">
        </component>
        <component id="userRepository" lifestyle="custom"
                   customLifestyleType="Common.Infrastructure.PerWebRequestLifestyleManager, Common.Castle"
                   service="Project.BL.Repository.IUserRepository, Project.BL"
                   type="Project.BL.Repository.UserRepository, Project.BL.LINQ">
            <parameters>
              <factory>${objectContextFactory}</factory>
            </parameters>
        </component>
    </components>
</castle>

To me, everything looks like it should. When I attempt to resolve an instance of the IObjectContextFactory service, I retrieve an ObjectContextFactory object. My problem comes in when I try and resolve an instance of the IUserRepository service. I am treated to the following delightful exception:

Can't create component 'userRepository' as it has dependencies to be satisfied. userRepository is waiting for the following dependencies:

Services:

- SandCastle.DAL.Context.IObjectContext which was not registered.

I've tried everything I can think of on this. So, unto you stackoverflow readers, I say: got any ideas?

Vienna answered 17/11, 2009 at 3:23 Comment(0)
V
7

This might be regarded as a bug (and indeed for cases like this it's fixable) but it's kindof a by-design feature.

Windsor tries to match the greediest constructor (one with the most parameters) it can satisfy.

However in your case, there are two constructors that have the greatest number of parameters (of one), so Windsor just picks the first, where what the "first" means is undefined.

indeed if you switch the order of your constructors in your source code your code will start working, although it's a hack, relying on undocumented behavior and don't do it.

Let's go back to where we started shall we?

I said Windsor is confused because there's no single greediest constructor it can satisfy.

Quick and well-defined fix would be to add a fake parameter to one of th constructors so that they have different numbers of parameters:

public class UserRepository : IUserRepository {
    public UserRepository(IObjectContext objectContext, object fakeIgnoreMe)  {
        // Check that the supplied arguments are valid.
        Validate.Arguments.IsNotNull(objectContext, "objectContext");
        // ignoring fake additional argument
        // Initialize the local fields.
        ObjectContext = objectContext;
    }

    public UserRepository(IObjectContextFactory factory) 
        : this(factory.CreateObjectContext()) { 
    }

    // -----------------------------------------------
    // Insert methods and properties...
    // -----------------------------------------------
}

Please report this issue to Castle users list or straight to issue tracker so that it will get fixed.

Vaticide answered 17/11, 2009 at 9:2 Comment(5)
Worked like gangbusters! Thanks!Vienna
"Windsor tries to match the greediest constructor (one with the most parameters it can satisfy)". I think it is more correct to say "from the list of constructors that only contain resolvable paramters, the constructor with the most parameters is picked".Safeconduct
@KrzysztofKoźmic: I think the adition of "(" and ")" does it :-)Safeconduct
@KrzysztofKoźmic: I like to discuss this constructor resolution behavior with you. Is there a channel through which we can communicate about this (mail for instance)?Safeconduct
how about the Castle users google group? That's generally the best placeVaticide
E
0

As of Windsor 3.2.x

If the attribute Castle.Core.DoNotSelectAttribute is applied to a constructor, it will not be selected, notwithstanding any other criteria.

public class UserRepository : IUserRepository
{
    [DoNotSelect] // This constructor will be ignored by Windsor
    public UserRepository(IObjectContext objectContext)
    {
        // ...
    }

    public UserRepository(IObjectContextFactory factory)
        : this(factory.CreateObjectContext()) {}
}

Reference: https://github.com/castleproject/Windsor/blob/86696989a7698c45b992eb6e7a67b765b48108b0/docs/how-constructor-is-selected.md

Elk answered 23/11, 2017 at 2:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.