【问题标题】:Resolving parent/child with Castle Windsor解决与温莎城堡的父母/孩子
【发布时间】:2011-08-13 16:05:33
【问题描述】:

我不确定如何称呼这个父母/孩子,但你去吧,我有一个类似的案例:

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 还不能注入这个属性吗?有什么方法可以完成这项工作,还是我应该手动运行一些代码来设置子对象的父母(这远非理想)?

提前致谢!

【问题讨论】:

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


    【解决方案1】:

    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 + Parameter 类型中 Void .ctor(Mike.DIDemo.Parent) 的服务依赖项“父”类型“Mike.DIDemo.Parent” Void 的依赖 'children' 类型 'Mike.DIDemo.IChild[]' Mike.DIDemo.Parent 中的 .ctor(Mike.DIDemo.IChild[])

    当您有所需的依赖项时,使用构造函数注入总是更好。使用属性注入告诉 Windsor 依赖项是可选的:如果可以,请提供组件,否则将属性保留为空。在这种情况下,首先解决了子项,因此在创建父依赖项时,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; }
    }
    

    【讨论】:

    • 很公平。我会走这条路,尽管它很臭。
    • 我想知道 Windsor 是否适合您手头的工作。看起来您正在创建域对象图。让 Windsor 创建域实例通常不是一个好主意,更常见的是让一些存储库提供它们。然后,您将向 Windsor 询问存储库而不是域实例。
    • 其实这里的Parent代表一个WPF屏幕的ViewModel,Child代表一个可以在该屏幕上调用的Action,可视化为Button控件。我的 ViewModel 应该知道可用的操作来呈现它们,并且每个操作都应该知道它们要与之交互的 ViewModel。这似乎是使用依赖容器的好案例,因为我有一些内置操作以及通过扩展提供的自定义操作。
    【解决方案2】:

    实际上可以在不调用其构造函数的情况下创建一个对象。它的所有字段都将为空,但您将拥有对对象的引用。此功能未在 Windsor 中实现,需要此功能的东西可能是一种设计味道。

    【讨论】:

      【解决方案3】:

      这是一个更好的变体:)

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多