代码之家  ›  专栏  ›  技术社区  ›  Damian Powell

如何避免城堡温莎的集合分解器的循环行为?

  •  2
  • Damian Powell  · 技术社区  · 14 年前

    我的申请中使用了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.Register(
        Component
            .For<IService>()
            .ImplementedBy<CompositeService>(),
    
        Component
            .For<IService>()
            .ImplementedBy<ConcreteService1>(),
    
        Component
            .For<IService>()
            .ImplementedBy<ConcreteService2>()
    );
    

    这种循环行为似乎与可用于实现decorator模式的标准(非基于集合)行为不一致,在这种情况下,将忽略要解析的组件,而使用实现相同接口的下一个注册组件。

    所以我想知道是否有办法让温莎排除 CompositeService 组件在解决 IService 服务 decoratedServices 财产。这个 装饰服务 属性应包含两个项:的实例 ConcreteService1 以及 ConcreteService2 . 我希望在不显式指定依赖项的情况下进行此操作。

    2 回复  |  直到 14 年前
        1
  •  0
  •   Aliostad    14 年前

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

    params

    IEnumerable<IService> decoratedServices

        2
  •  0
  •   Damian Powell    14 年前

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