我在申请中使用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
服务时,是否有办法让Windsor排除decoratedServices
组件。 decoratedServices
属性应包含两个项:ConcreteService1
的实例和ConcreteService2
的实例。我想在没有明确指定依赖项的情况下这样做。
答案 0 :(得分:0)
我知道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)我删除params 2)我将构造函数更改为IEnumerable<IService> decoratedServices
3)我创建另一个类并实现IEnumerable,这只是一个工厂将返回给我定义的类型列表,我使用它们的名称初始化。
答案 1 :(得分:0)
这是我目前的解决方法。我对Windsor的内部结构并不太熟悉,因此这个解决方案可能存在明显的错误,所以使用它需要您自担风险!
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);
}
}