关于控制反转:如何以编程方式注册依赖于 Castle Windsor 已注册组件列表的组件?

How to programmatically register a component that depends on a list of already registered components with Castle Windsor?

我正在以编程方式注册一组服务,这些服务都实现了相同的接口 IRule。我有另一个看起来像这样的服务:

1
2
3
4
5
6
public class MyService {
    private IEnumerable<IRule> _rules;
    public MyService(IEnumerable<IRule> rules){
        _rules = rules;
    }
}

Hammett 发布了一些我想要的东西,http://hammett.castleproject.org/?p=257。我将签名更改为 IRule[] 并在帖子中尝试了 ArrayResolver 技巧,但这对我不起作用(注意,它也没有破坏任何东西)。

有人知道如何像我上面发布的代码那样以编程方式注册组件吗?


如果您不想更改 MyService 的签名并继续使用 IEnumerable<IRule>,您还可以创建自定义 ISubDependencyResolver。这就是我们所做的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class EnumerableResolver : ISubDependencyResolver
{
    private readonly IKernel kernel;

    public EnumerableResolver(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        Type targetType = dependency.TargetType;
        if (targetType == null)
        {
            throw new ArgumentException("TargetType property cannot be null","dependency");
        }

        if (targetType.IsGenericType && (targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
        {
            Type service = targetType.GetGenericArguments()[0];
            return this.kernel.HasComponent(service);
        }
        return false;
    }

    public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        Type service = dependency.TargetType.GetGenericArguments()[0];
        Array array = this.kernel.ResolveAll(service, (IDictionary)null);
        return Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { service }), new object[] { array });
    }
}

它需要像这样在容器中注册:

1
container.Kernel.Resolver.AddSubResolver(new EnumerableResolver(this.Kernel));

我加载了 Castle.MicroKernel 的源代码,并注意到已经有一个 ArrayResolver 和 ListResolver(在 Castle.MicroKernel.Resolvers.SpecializedResolvers 命名空间中)。我从 Hammet 的博客中(盲目地)复制的代码不起作用,很可能是因为框架在编写后发生了变化。

这里有一个示例项目演示如何执行此操作:http://www.panteravb.com/downloads/WindsorCon.zip

我尝试了 ArrayResolver 和 ListResolver,它们都可以顺利运行,这非常简单,所以假设这个服务类:

1
2
3
4
5
6
7
8
public class MyService
{
    private IEnumerable<IRule> _rules;
    public MyService(IList<IRule> rules)
    {
        _rules = rules;
    }
}

你可以这样注册这个人:

1
2
3
4
5
6
7
8
9
private IWindsorContainer _container;
private void InitializeIoc()
{
    _container = new WindsorContainer();
    _container.Kernel.Resolver.AddSubResolver(new ListResolver(_container.Kernel));
    _container.Register(Component.For<IRule>().ImplementedBy<Rule1>());
    _container.Register(Component.For<IRule>().ImplementedBy<Rule2>());
    _container.Register(Component.For<MyService>());
}