2
votes

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.

1
Does IModule have the InnerModules property?fra9001
No. InnerModules is specific to the compositemodule implementation. Not all modules have this hierarchy. I will edit to make this clear.unklegwar
There you go.fra9001
Sorry. What does "There you go" mean?unklegwar
Oh. I didn't notice it was a link!unklegwar

1 Answers

4
votes

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