【问题标题】:Castle Windsor Resolve method: why pass arguments? What are they for?Castle Windsor Resolve 方法:为什么要传递参数?它们是干什么用的?
【发布时间】:2013-08-04 22:26:20
【问题描述】:

我对 Castle Windsor 解决方法感到困惑。这种方法让我几乎可以通过任何东西。在 resolve 方法中提交的值是传递并在最终解析为的对象的构造函数中使用,还是用于帮助解析器确定要使用的具体实现?

例如,如果我有以下sn-p...

var _container = new WindsorContainer();
_container.Install(FromAssembly.This());

var MyProcessor = _container.Resolve<IProcessor>(new Arguments(new {"Processor1"}));

假设我有两个 IProcessor 的具体实现 - 例如 Processor1:IProcessor 和/或 Processor2:IProcessor。 “参数”有什么用?

我明白...

Component.For<IProcessor>() 

... 需要定义,但我正在为温莎人选择使用的术语(即 DependsOn 或 ServicesOverrides)和意图而苦苦挣扎。鉴于该方法称为“resolve”,我只能将传递给它的任何值用于解决使用哪个具体实现的决定。这个假设是错误的吗?

【问题讨论】:

    标签: castle-windsor


    【解决方案1】:

    您所说的arguments参数是为Windsor组件无法满足的组件提供参数。匿名类型重载以及我相信的字典工作服都是为了这个目的。我过去曾使用过它,但我不推荐它,因为它会导致像 Cristiano 提到的那样糟糕的模式……而上次我使用它时,它仅适用于直接解析的组件。无论如何......这是一个如何工作的例子:

    [TestFixture]
    public class Fixture
    {
        [Test]
        public void Test()
        {
            IWindsorContainer container = new WindsorContainer();
            container.Register(Component.For<IFoo>().ImplementedBy<Foo>().LifeStyle.Is(LifestyleType.Transient));
    
            Assert.Throws<HandlerException>(() => container.Resolve<IFoo>());
    
            IFoo foo = container.Resolve<IFoo>(new {arg1 = "hello", arg2 = "world"});
            Assert.That(foo, Is.InstanceOf<Foo>());
            Assert.That(foo.ToString(), Is.EqualTo("hello world"));
        }
    }
    
    public interface IFoo
    {
    
    }
    
    public class Foo : IFoo
    {
        private readonly string _arg1;
        private readonly string _arg2;
    
        public Foo(string arg1, string arg2)
        {
            _arg1 = arg1;
            _arg2 = arg2;
        }
    
        public override string ToString()
        {
            return string.Format("{0} {1}", _arg1, _arg2);
        }
    }
    

    【讨论】:

    • 您是说在resolve方法中传递的任何值都将用于满足正在解析的对象的构造函数参数?我发现在 Castle Windsor 3 中,传入 resolve 的值有助于 Castle 确定要使用的具体实现(假设注册的组件在 name 方法中定义了一个值)。这与您的建议完全不同-并不是说您不正确,因为可能有多种用途。这是我试图确定的。您是否尝试过使用这个提供的值来推导具体的实现?
    • 字典、匿名类型和 Argument 参数重载用于我上面显示的内容。 “key”参数重载用于通过键解析指定注册。在 Windsor 的最新版本中,为您的注册分配密钥是可选的(如果您对每种服务类型进行多次注册,则仍然需要密钥)。重读您的问题后,听起来您需要为您的注册分配一个密钥,然后在 Resolve 方法上传递一个密钥...
    • 或者使用 IHandlerSelector 之类的东西来处理分辨率。这取决于你的架构和我运行时知道的应该解决的问题......但你应该尽可能避免从容器中“拉”。
    【解决方案2】:

    我将答案授予 kellyb 的真棒示例。在调查过程中,使用 Castle.Windsor 3.2.1,我发现至少有两个原因需要在“resolve”方法中传递值。

    1. 满足内在的类型依赖,例如字符串或整数 使用“Resolve”方法解析的对象 - 如 在 kellyb 的示例中进行了描述。
    2. 帮助 Castle 确定要选择的具体实现。

    为了帮助说明这两种用途,我正在详细说明上面由 kellyb 提供的示例。

    概要 - 或测试条件

    假设有一个名为 IFoo 的接口和两个从该接口派生的具体实现,名为 Foo 和 Bar。定义了一个名为 Baz 的类,但它不派生自任何东西。假设 Foo 需要两个字符串,但 Bar 需要一个 Baz。

    接口 IFoo 定义

    namespace CastleTest
    {
        public interface IFoo
        {
        }
    }
    

    类 Foo 定义

    namespace CastleTest
    {
        public class Foo : IFoo
        {
            private readonly string _arg1;
            private readonly string _arg2;
    
            public Foo(string arg1, string arg2)
            {
                _arg1 = arg1;
                _arg2 = arg2;
            }
    
            public override string ToString()
            {
                return string.Format("{0} {1}", _arg1, _arg2);
            }
        }
    }
    

    类栏定义

    namespace CastleTest
    {
        class Bar : IFoo
        {
            private Baz baz;
    
            public Bar(Baz baz)
            {
                this.baz = baz;
            }
    
            public override string ToString()
            {
                return string.Format("I am Bar.  Baz = {0}", baz);
            }
        }
    }
    

    类 Baz 定义

    namespace CastleTest
    {
        public class Baz
        {
            public override string ToString()
            {
                return "I am baz.";
            }
        }
    }
    

    测试(请打鼓!)

    kellyb 的示例测试显示了一个断言,如果未提供 args,则该断言会失败。 kellyb 的示例没有注册多个实现。我的示例注册了多个实现,并且根据哪个被标记为默认值,此断言可能会失败,也可能不会失败。例如,如果名为“AFooNamedFoo”的具体实现被标记为默认,则断言成功完成 - 也就是说,将 IFoo 解析为 Foo 确实需要定义 args。如果名为“AFooNamedBar”的具体实现被标记为默认值,则断言失败——也就是说,将 IFoo 解析为 Bar 不需要定义 args,因为它对 Baz 的依赖已经注册(在我的示例中注册了多个具体实现)。因此,我在示例中注释掉了断言。

    using Castle.Core;
    using Castle.MicroKernel.Handlers;
    using Castle.MicroKernel.Registration;
    using Castle.Windsor;
    using NUnit.Framework;
    
    namespace CastleTest
    {
        [TestFixture]
        public class ArgsIdentifyConcreteImplementation
        {
            [Test]
            public void WhenSendingArgsInResolveMethodTheyAreUsedToIdentifyConcreteImplementation()
            {
                IWindsorContainer container = new WindsorContainer();
                container.Register(Component.For<IFoo>().ImplementedBy<Foo>().LifeStyle.Is(LifestyleType.Transient).Named("AFooNamedFoo"));
                container.Register(Component.For<IFoo>().ImplementedBy<Bar>().LifeStyle.Is(LifestyleType.Transient).Named("AFooNamedBar").IsDefault());
                container.Register(Component.For<Baz>().ImplementedBy<Baz>().LifeStyle.Is(LifestyleType.Transient));
    
                // THIS ASSERT FAILS IF AFooNamedBar IS DEFAULT, BUT
                // WORKS IF AFooNamedFoo IS DEFAULT
                //Assert.Throws<HandlerException>(() => container.Resolve<IFoo>());
    
                // RESOLVE A FOO
                IFoo foo = container.Resolve<IFoo>("AFooNamedFoo", new { arg1 = "hello", arg2 = "world" });
                Assert.That(foo, Is.InstanceOf<Foo>());
                Assert.That(foo.ToString(), Is.EqualTo("hello world"));
    
                // RESOLVE A BAR
                IFoo bar = container.Resolve<IFoo>("AFooNamedBar");
                Assert.That(bar, Is.InstanceOf<Bar>());
                Assert.That(bar.ToString(), Is.EqualTo("I am Bar.  Baz = I am baz."));
            }
        }
    }
    

    结论

    看上面的测试,一个 Foo 对象的解析在“resolve”方法中传递了两件事——实现的名称,以及作为 IDictionary 对象的附加字符串依赖项。 Bar 对象的解析在“resolve”方法中传递了一件事——实现的名称。

    【讨论】:

      【解决方案3】:

      事实上,您不应该在代码中而不是在Composition root 中调用 Resolve,而且您也不需要为 Resolve 方法提供参数。

      应通过安装程序、工厂/ITypedFactoryComponentSelector、子解析器完成自定义解析策略...有关这些选项的更多详细信息,请参阅documentation

      顺便说一句,通过“Resolve”参数,您可以识别要解析的组件(按名称或类型)及其自身的直接依赖关系。

      【讨论】:

      • 是的,文档很简单。我有一种情况,具体实现具有基于接口的多个依赖项 - 这些都具有基于接口的多个依赖项。我还没有看到使用工厂方法的一个很好的简单示例。子解析器需要在方法“resolve”中返回时定义依赖链,这违背了 DI 容器的目的。我不知道,我可能完全错误地接近这个。我偶然发现了一个使用 DependsOn 的示例,这确实很有帮助。根据运行时数据在 Composition 根中调用 Resolve
      • 关于工厂阅读:docs.castleproject.org/…
      猜你喜欢
      • 2011-08-17
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-26
      相关资源
      最近更新 更多