Циклическая зависимость через внедрение свойств в Castle Windsor

Мне нужна циклическая зависимость в Виндзоре. Это сделано специально для рекурсивного подхода. Модуль A вызывает модули B-F, а модулю B требуется обратный вызов в A из-за дерева узлов.

В любом случае, я знаю, что из-за этого не могу использовать инъекцию конструктора. Итак, я настроил модуль A с внедрением свойств, чтобы получить IEnumerable ‹IModule›.

Я зарегистрировал все типы, но при разрешении модуля A Виндзор устанавливает для свойства значение Null (у меня возникает исключение). Я пробовал использовать DependsOn, но, похоже, это применимо только к аргументам конструктора.

Как я могу заставить Windsor преобразовать мою коллекцию модулей B-F в модуль A с помощью инъекции свойств, чтобы я мог получить нужный мне цикл зависимостей?

Вот модуль верхнего уровня (Модуль A) и 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));
    }

}

И рекурсивный подмодуль:

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!

    }
}

И моя регистрация:

// 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.

Как я уже сказал, я попробовал DependsOn, который, похоже, не применяется к свойствам, а также PropertyRequire, который не имел никакого эффекта, мне все равно было передано значение null.

Обратите внимание, что у меня нет доступа к коду Container.Resolve, поскольку я регистрирую эти компоненты для стороннего инструмента.

Спасибо!

ИЗМЕНИТЬ

Чего я жду:

  1. Windsor разрешает CompositeModule и создает экземпляр, используя ctor по умолчанию. Называет это.

  2. Виндзор идет, чтобы удовлетворить свойство, которое является перечислимым из IModules

  3. Он видит 6 IModules, не считая CompositeModule, и пытается создать их экземпляры в коллекции.

  4. Он видит, что для одного из 6 модулей, CheckChildrenModule, требуется именованный экземпляр CompositeModule.

  5. Он использует именованный экземпляр CompositeModule, созданный на шаге 1, чтобы удовлетворить зависимость ctor от CheckChildrenModule.

  6. Теперь он может завершить сбор IModules и передать их в свойство InnerModules CompositeModule, начиная с шага 2.

Просто не знаю, как этого добиться.


person unklegwar    schedule 23.07.2014    source источник
comment
Есть ли у IModule свойство InnerModules?   -  person fra9001    schedule 23.07.2014
comment
Нет. InnerModules специфичен для реализации составного модуля. Не все модули имеют такую ​​иерархию. Я отредактирую, чтобы прояснить это.   -  person unklegwar    schedule 23.07.2014
comment
Итак, вы идете.   -  person fra9001    schedule 23.07.2014
comment
Прости. Что значит "Там ты идёшь"?   -  person unklegwar    schedule 23.07.2014
comment
Ой. Я не заметил, что это ссылка!   -  person unklegwar    schedule 23.07.2014
comment
Я не могу использовать инъекцию Ctor из-за циклической зависимости. И здесь нет базового / производного класса. Я не уверен, как применим этот связанный вопрос.   -  person unklegwar    schedule 23.07.2014
comment
Позвольте нам продолжить это обсуждение в чате.   -  person fra9001    schedule 23.07.2014


Ответы (1)


Я считаю, что вы можете использовать ленивое разрешение с инъекцией конструктора для достижения своей циклической зависимости.

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();
    }
}
person TylerOhlsen    schedule 24.07.2014
comment
Разве это не создало бы НОВЫЙ ICompositeModule для CheckChildrenModule? Это просто запустит цикл заново, потому что составному модулю требуется экземпляр CheckCHildrenModule в качестве одного из его внутренних компонентов. Кроме того, CompositeModule, переданный в CheckChildrenModule, должен быть тем же самым, который сам содержит ссылку на CheckChildrenModule. Это не может быть новый, созданный лениво. - person unklegwar; 24.07.2014
comment
Ленивое разрешение будет уважать любой образ жизни, который вы установили во время регистрации ICompositeModule. Он создаст новый экземпляр только в том случае, если вы зарегистрировали композит как временный (что верно, если вы используете любой другой тип инъекции). - person TylerOhlsen; 24.07.2014
comment
Я не знаком с этой способностью Виндзора. Это буквально весь код, который мне нужен для подключения? Как указать, что именованный экземпляр композитного модуля будет использоваться в параметре lazy? Кроме того, поскольку составной модуль должен быть первым зарегистрированным IModule (чтобы он был разрешен первым), мне нужно что-либо изменить в моей регистрации, чтобы поддержать это? - person unklegwar; 24.07.2014
comment
Также есть ли причина переключить CompositeModule на ICompositeModule, а не на IModule? - person unklegwar; 24.07.2014
comment
Это сработало. Я даже придумал, как его тестировать. Я был бы рад, если бы вы могли объяснить волшебство предоставленной вами регистрации. Каким образом ленивый загрузчик компонентов попадает в микс, если я не указывал его где-либо еще? - person unklegwar; 24.07.2014
comment
Он работает аналогично ArrayResolver (но немного отличается, поскольку технически не является суб-преобразователем). Это специальный класс, встроенный в ядро ​​Windsor с пользовательским кодом в ядре для удовлетворения его особых потребностей. - person TylerOhlsen; 25.07.2014