与Castle Windsor一起解决父母/子女问题

时间:2011-04-27 08:42:38

标签: c# dependency-injection castle-windsor ioc-container circular-dependency

我不确定是否打电话给这位父母/孩子,但在这里,我有类似的情况:

namespace ConsoleApplication1
{
    using System.Diagnostics;
    using System.Linq;
    using Castle.MicroKernel.Registration;
    using Castle.MicroKernel.Resolvers.SpecializedResolvers;
    using Castle.Windsor;

    class Program
    {
        static void Main(string[] args)
        {
            var container = new WindsorContainer();

            container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

            container.Register(
                Component.For<Parent>().LifeStyle.Singleton,
                Component.For<IChild>().ImplementedBy<Child1>().LifeStyle.Singleton);

            var p = container.Resolve<Parent>();

            // Fails...
            Debug.Assert(p.Children.First().Parent == p, "Parent should be resolved");
        }
    }

    class Parent
    {
        public IChild[] Children { get; set; }
    }

    interface IChild
    {
        Parent Parent { get; set; }
    }

    class Child1 : IChild
    {
        public Parent Parent { get; set; }
    }
}

我已将CollectionResolver添加到容器中。 Parent和Child1(使用IChild服务)都在容器中注册为单例。每当我尝试解析Parent实例时,我都会填充我的Children数组,但该数组中的Child1实例的Parent为null。我期待的是将Child1的Parent属性设置为我当时尝试解析的Parent实例。我可以理解Parent尚未完全激活,但是由于它的ctor运行,Windsor不能注入这个属性吗?有没有办法让这项工作或我应该手动运行一些代码来设置子对象的父项(这是不是很理想)?

提前致谢!

3 个答案:

答案 0 :(得分:2)

Windsor不会让你创建一个循环依赖链。如果您更改了子定义和父定义,以便它们使用构造函数注入而不是属性注入,如下所示:

class Parent
{
    public Parent(IChild[] children)
    {
        Children = children;
    }

    public IChild[] Children { get; private set; }
}

interface IChild
{
    Parent Parent { get; }
}

class Child1 : IChild
{
    public Child1(Parent parent)
    {
        Parent = parent;
    }

    public Parent Parent { get; private set; }
}

当你现在运行测试时,你会发现Windsor抱怨依赖循环:

  

测试'M:Mike.DIDemo.WindsorSpike.ParentChild'失败:一个周期是   在尝试解决依赖关系时检测到。依赖图   导致一个周期是:     - 参数依赖性'children'类型'Mike.DIDemo.IChild []'代表Mike.DIDemo.Parent类型中的Void .ctor(Mike.DIDemo.IChild [])     - 服务依赖性'parent'类型'Mike.DIDemo.Parent'代表Mike.DIDemo.Child1 +参数中的Void .ctor(Mike.DIDemo.Parent)   Void的依赖'儿童'类型'Mike.DIDemo.IChild []'   Mike.DIDemo.Parent中的.ctor(Mike.DIDemo.IChild [])

当您具有所需的依赖项时,最好使用构造函数注入。使用属性注入告诉Windsor依赖项是可选的:如果可以,请提供组件,否则只需将属性保留为null。在这种情况下,孩子们首先得到解决,所以当创建父依赖关系时,Windsor看到了一个循环,并将其保留为空。

这里的解决方案是在解析子项时填充Parent,方法是在Parent构造函数中放入一些代码。

class Parent
{
    public Parent(IChild[] children)
    {
        Children = children;
        foreach (var child in children)
        {
            child.Parent = this;
        }
    }

    public IChild[] Children { get; private set; }
}

interface IChild
{
    Parent Parent { get; set;  }
}

class Child1 : IChild
{
    public Parent Parent { get; set; }
}

答案 1 :(得分:0)

实际上可以在不调用其构造函数的情况下创建对象。它的所有字段都将为null,但您将获得对象的引用。 Windsor没有实现此功能,需要这个功能可能是一种设计气味。

答案 2 :(得分:0)

这是一个更好一点的变化:)

container.Register(Component.For<SearchCommand>());
container.Register(Component.For<ShowOptionsCommand>());
container.Register(Component.For<MainWindowViewModel>().OnCreate(new Action<MainWindowViewModel>(p => p.SetUpCommands())));

public class MainWindowViewModel
{
    public ShowOptionsCommand ShowOptions { get; set; }
    public SearchCommand Search { get; set; }

    public MainWindowViewModel()
    {
    }

    public void SetUpCommands()
    {
        this.ShowOptions.Host = this;
        this.Search.Host = this;
    }
}