Как избежать циклического поведения с CollectionResolver Касла Виндзора? - PullRequest
2 голосов
/ 17 сентября 2010

Я использую Castle Windsor 2.5 в своем приложении.У меня есть сервис, который является составным, который действует как распределитель для объектов, которые реализуют тот же интерфейс.

public interface IService { void DoStuff(string someArg); }

public class ConcreteService1 : IService {
    public void DoStuff(string someArg) { }
}

public class ConcreteService2 : IService {
    public void DoStuff(string someArg) { }
}

public class CompositeService : List<IService>, IService
{
    private readonly IService[] decoratedServices;

    CompositeService(params IService[] decoratedServices) {
        this.decoratedServices = decoratedServices;
    }

    public void DoStuff(string someArg) {
        foreach (var service in decoratedServices) {
            service.DoStuff(someArg);
        }
    }
}

У меня проблема в том, что использование конфигурации, такой как показано ниже, заставляет Windsor сообщать, чтоЦикл был обнаружен при попытке разрешить зависимость. "

windsor.Register(
    Component
        .For<IService>()
        .ImplementedBy<CompositeService>(),

    Component
        .For<IService>()
        .ImplementedBy<ConcreteService1>(),

    Component
        .For<IService>()
        .ImplementedBy<ConcreteService2>()
);

Это циклическое поведение противоречит стандартному (не основанному на сборе) поведению, которое можно использовать для улучшения шаблона декоратора, гдеразрешаемый компонент игнорируется, и вместо него используется следующий зарегистрированный компонент, реализующий тот же интерфейс.

Итак, я хотел бы знать, есть ли способ заставить Windsor исключить компонент CompositeServiceкогда он разрешает службы IService для свойства decoratedServices.Свойство decoratedServices должно содержать два элемента: экземпляр ConcreteService1 и экземпляр ConcreteService2.Я хотел бы сделать это без явного указания зависимостей.

Ответы [ 2 ]

0 голосов
/ 17 сентября 2010

Вот мой текущий обходной путь.Я не слишком знаком с внутренностями Виндзора, поэтому в этом решении могут быть явные ошибки, поэтому используйте его на свой страх и риск!

public class NonCyclicCollectionResolver : ISubDependencyResolver
{
    private readonly IKernel kernel;
    private readonly bool    allowEmptyCollections;

    public NonCyclicCollectionResolver(
        IKernel kernel,
        bool    allowEmptyCollections = false
    ) {
        this.kernel                = kernel;
        this.allowEmptyCollections = allowEmptyCollections;
    }

    public bool CanResolve(
        CreationContext        context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel         model,
        DependencyModel        dependency
    ) {
        if (dependency.TargetType == null) return false;

        var itemType = dependency.TargetType.GetCompatibileArrayItemType();
        if (itemType == null) return false;

        if (!allowEmptyCollections)
        {
            return GetOtherHandlers(itemType, model.Name).Any();
        }

        return true;
    }

    public object Resolve(
        CreationContext        context,
        ISubDependencyResolver contextHandlerResolver,
        ComponentModel         model,
        DependencyModel        dependency
    ) {
        var itemType = dependency.TargetType.GetCompatibileArrayItemType();

        var handlers = GetOtherHandlers(itemType, model.Name);

        var resolved = handlers
            .Select(h => kernel.Resolve(h.ComponentModel.Name, itemType))
            .ToArray();

        var components = Array.CreateInstance(itemType, resolved.Length);
        resolved.CopyTo(components, 0);
        return components;
    }

    private IEnumerable<IHandler> GetOtherHandlers(
        Type   serviceType,
        string thisComponentName
    ) {
        return kernel
            .GetHandlers(serviceType)
            .Where(h => h.ComponentModel.Name != thisComponentName);
    }
}
0 голосов
/ 17 сентября 2010

Я знаю Unity, но я считаю, что ваша проблема носит общий характер и относится ко всем системам DI. Большинство инструментов DI имеют возможность явно называть вашу зависимость. Это особенно полезно, если вы реализуете тот же интерфейс.

Так что я считаю, что ваша проблема не столько в цикличности (если есть такое слово) зависимости, сколько в ее множественности. Поэтому я бы назвал зависимости следующим образом:

windsor.Register( 
Component 
    .For<IService>() 
    .ImplementedBy<CompositeService>("CompositeService"), 

Component 
    .For<IService>() 
    .ImplementedBy<ConcreteService1>("ConcreteService1"), 

Component 
    .For<IService>() 
    .ImplementedBy<ConcreteService2>("ConcreteService2") 

);

Но я не вижу, как ваша DI-инфраструктура может обрабатывать конструктор вашего CompositeService. params жесткая.

Вот что я бы сделал:

1) Я удаляю параметры 2) Я изменяю конструктор на IEnumerable<IService> decoratedServices 3) Я создаю другой класс и реализую IEnumerable, который является просто фабрикой, вернет мне список определенных типов, которые я инициализирую, используя их имена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...