2

I have a need for a cyclic dependency in Windsor. This is on purpose for a recursive approach. Module A calls Modules B-F and Module B needs to call back into A because of a tree of nodes.

Anyway, I know I can't use constructor injection because of this. So I set up Module A with property injection to get an IEnumerable<IModule>.

I've registered all of the types but when resolving Module A, Windsor is setting the property with Null (I have it throw an exception). I tried using DependsOn, but that seems to apply only to constructor arguments.

How can I get Windsor to resolve my collection of modules B-F into module A via property injection so I can have the dependency cycle I need?

Here's the top level module (Module A) and IModule;

public interface IModule
{
    bool IsAccessibleToUser(INode node);
}

public class CompositeModule:IModule
{
    private IEnumerable<IModule> innerModules;

    public IEnumerable<IModule> InnerModules
    {
        get { return innerModules; }
        set { 
            if (null == innerModules) throw new ArgumentNullException("innerModules");
            innerModules = value;
        }
    }

    public CompositeModule()
    {
        InnerModules = new List<IModule>();
    }


    public bool IsAccessibleToUser(INode node)
    {
        return innerModules.All(module => module.IsAccessibleToUser(node));
    }

}

And the recursive submodule:

public class CheckChildrenModule : IModule
{

    private IModule compositeModule;

    public CheckChildrenModule(IModule compositeModule)
    {

        if (null == compositeModule) throw new ArgumentNullException("compositeModule");

        this.compositeModule = compositeModule;

    }

    public bool IsAccessibleToUser(INode node)
    {

        var attributes = node.Attributes;

        if (!attributes.ContainsKey("checkchildren")) return true;

        string checkchildren = attributes["checkchildren"].ToString();

        if (checkchildren.ToLower() == bool.TrueString.ToLower())
        {
            //look for any child with result of TRUE and stop immediately if found in order to be efficient.
            return node.ChildNodes.Any(childNode => compositeModule.IsAccessibleToUser(childNode));
        }

        return true; //check false or some other value. return true!

    }
}

And my registration:

// Configure Windsor to resolve arrays in constructors
container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel, true));
//collections too
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

//other stuff here

container.Register(Component.For<IModule>()
            .ImplementedBy<CompositeModule>()
            .Named("CompositeModule1"));

container.Register(Component.For<IModule>().ImplementedBy<CheckChildrenModule>()
            .DependsOn(ServiceOverride.ForKey<IModule>().Eq("CompositeModule1")));

//A few other IModules here, all of which should be passed to the Composite Module "innerModules" property.

As I said, I tried DependsOn, which doesn't seem to apply to properties, and also PropertyRequire, which had no effect, I still got passed a null.

Note that I do not have access to the Container.Resolve code, as I'm registering these components for a 3rd party tool.

Thanks!

EDIT

What I expect:

  1. Windsor resolves CompositeModule and creates an instance using default ctor. Names it.

  2. Windsor goes to satisfy the property, which is an enumerable of IModules

  3. It sees 6 IModules, not counting CompositeModule, and tries to instantiate them into a collection

  4. It sees that one of the 6 modules, CheckChildrenModule, requires the named instance of CompositeModule.

  5. It uses the named instance of CompositeModule created in Step 1 to satisfy the ctor dependency on CheckChildrenModule

  6. Now it can finish the collection of IModules and pass them into the InnerModules property of CompositeModule, from step 2.

Just don't know how to get it to happen.

unklegwar
  • 135
  • 1
  • 6
  • Does `IModule` have the `InnerModules` property? –  Jul 23 '14 at 15:29
  • No. InnerModules is specific to the compositemodule implementation. Not all modules have this hierarchy. I will edit to make this clear. – unklegwar Jul 23 '14 at 15:31
  • [There](http://stackoverflow.com/questions/20427882/castle-windsor-ioc-property-injection-using-property-from-baseclass-inside-chil) you go. –  Jul 23 '14 at 15:34
  • Sorry. What does "There you go" mean? – unklegwar Jul 23 '14 at 15:48
  • Oh. I didn't notice it was a link! – unklegwar Jul 23 '14 at 16:40
  • I can't use Ctor injection because of the circular dependency. And there's no base class/derived class going on here. I'm not sure how that linked question applies. – unklegwar Jul 23 '14 at 16:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/57864/discussion-between-fra9001-and-unklegwar). –  Jul 23 '14 at 16:45

1 Answers1

4

I believe you could use lazy resolution with constructor injection to achieve your circular dependency.

container.Register(Component.For<ILazyComponentLoader>()
                            .ImplementedBy<LazyOfTComponentLoader>());

public class CompositeModule : ICompositeModule
{
    private IEnumerable<IModule> _childModules;
    public CompositeModule(IEnumerable<IModule> childModules) 
    {
        _childModules = childModules;
    }
}

public class CheckChildrenModule : IModule
{
    private Lazy<ICompositeModule> _compositeModule;
    public CheckChildrenModule(Lazy<ICompositeModule> compositeModule)
    {
        _compositeModule = compositeModule;
    }

    public void DoStuff() 
    {
        _compositeModule.Value.DoStuff();
    }
}
TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
  • Wouldn't this create a NEW ICompositeModule for the CheckChildrenModule? Which would just start the cycle all over again, because the composite module needs an instance of CheckCHildrenModule as one of its inners. Also, the CompositeModule passed to CheckChildrenModule has to be the very same one that itself holds a reference to CheckChildrenModule. It can't be a new one created lazily. – unklegwar Jul 24 '14 at 13:21
  • The lazy resolution will respect whatever lifestyle you have set during the registration of the ICompositeModule. It will only create a new instance if you registered the composite as transient (which is true if you use any other type of injection). – TylerOhlsen Jul 24 '14 at 13:46
  • I'm not familiar with this capability of Windsor. Is this literally all the code I need to get this wired up? How do I specify the named instance of the compositeModule be used in the lazy parameter? Also, since the composite module must be the first registered IModule (so that it is the one that is resolved first) do I need to change anything there in my registration to support this? – unklegwar Jul 24 '14 at 15:32
  • Also is there a reason to switch CompositeModule to be ICompositeModule as opposed to IModule? – unklegwar Jul 24 '14 at 15:39
  • It worked. I even figured out how to unit test it. I would love if you could explain the magic of the registration you provided. How does the lazy component loader get into the mix without me specifying it anywhere else? – unklegwar Jul 24 '14 at 16:10
  • It works similar to the ArrayResolver (but a little different since it is not technically a sub-resolver). This is a special class built into the Windsor kernel with custom code in the kernel to handle its special needs. – TylerOhlsen Jul 25 '14 at 16:48