C# 使用Castle Windsor解析父级/子级

C# 使用Castle Windsor解析父级/子级,c#,dependency-injection,castle-windsor,ioc-container,circular-dependency,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;

我不确定是否给这位家长/孩子打电话,但我有一个类似的案例:

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; }
    }
}
命名空间控制台应用程序1
{
使用系统诊断;
使用System.Linq;
使用Castle.MicroKernel.Registration;
使用Castle.MicroKernel.Resolvers.SpecializedResolver;
使用温莎城堡;
班级计划
{
静态void Main(字符串[]参数)
{
var container=新的WindsorContainer();
container.Kernel.Resolver.AddSubResolver(新的CollectionResolver(container.Kernel));
集装箱。登记(
Component.For().LifeStyle.Singleton,
Component.For().ImplementedBy().LifeStyle.Singleton);
var p=container.Resolve();
//失败。。。
Assert(p.Children.First().Parent==p,“应该解析父项”);
}
}
班级家长
{
公共IChild[]子项{get;set;}
}
接口IChild
{
父{get;set;}
}
一类儿童:一号
{
公共父级{get;set;}
}
}
我已将CollectionResolver添加到容器中。父对象和子对象1(使用IChild服务)都在容器中注册为单例。每当我尝试解析父实例时,都会填充我的Children数组,但该数组中的Child1实例的父实例为null。我希望Child1的Parent属性设置为我当时试图解析的父实例。我可以理解,父级尚未完全激活,但由于其ctor已运行,Windsor还不能注入此属性吗?是否有任何方法可以实现这一点,或者我应该手动运行一些代码来设置子对象的父对象(这远非理想)


提前谢谢

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”失败:一个周期已结束 尝试解析依赖项时检测到。依赖关系图 导致循环的原因是: -Mike.DIDemo.Parent类型中Void.ctor(Mike.DIDemo.IChild[])的参数依赖项“children”类型为“Mike.DIDemo.IChild[]” -Mike.DIDemo.Child1+参数类型中Void.ctor(Mike.DIDemo.parent)的服务依赖项“parent”类型为“Mike.DIDemo.parent” 依赖项“children”为Void键入“Mike.DIDemo.IChild[]” Mike.DIDemo.Parent中的.ctor(Mike.DIDemo.IChild[])

当您具有所需的依赖项时,最好使用构造函数注入。使用属性注入告诉Windsor依赖项是可选的:如果可以,请提供组件,否则只需将属性保留为null即可。在本例中,首先解析子项,因此当创建父依赖项时,Windsor看到将产生一个循环,并将其保留为空

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

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

实际上,不调用构造函数就可以创建对象。它的所有字段都将为空,但您将有一个对对象的引用。这项功能在温莎没有实现,需要这项功能的东西可能是一种设计味道。

这是一种更好的变体:)

container.Register(Component.For());
container.Register(Component.For());
container.Register(Component.For().OnCreate(新操作(p=>p.SetUpCommands()));
公共类MainWindowViewModel
{
public ShowOptions命令ShowOptions{get;set;}
公共搜索命令搜索{get;set;}
公共主窗口视图模型()
{
}
公共void SetUpCommands()
{
this.ShowOptions.Host=this;
this.Search.Host=this;
}
}

很公平。我会走这条路,尽管它有气味。我真的想知道温莎是否是你手头工作的合适工具。看起来您正在创建域对象图。让Windsor创建域实例通常不是一个好主意,更常见的是由一些存储库提供它们。然后,您将向Windsor请求存储库,而不是域实例。实际上,这里的父对象表示WPF屏幕的ViewModel,子对象表示可在该屏幕上调用的操作,该屏幕显示为按钮控件。我的ViewModel应该知道可以显示它们的可用操作,并且每个操作都应该知道它们与之交互的ViewModel。这似乎是一个可以使用依赖项容器的情况,因为我有一些内置操作,也有通过扩展提供的自定义操作。