Castle Windsor Fluent API:将具有单个项的数组定义为依赖项 [英] Castle Windsor Fluent API: Define Array with Single item as Dependency

查看:21
本文介绍了Castle Windsor Fluent API:将具有单个项的数组定义为依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

鉴于此 XML 配置(有效)

<参数><听众><阵列><item>${DefaultActivityListener}</item></阵列></听众></参数></组件><component type="X.Y.Z.DefaultActivityListener, X.Y.Z.Services" id="DefaultActivityListener" living="transient"/>

我已转换为使用如下 fluent API(这不起作用):

Container.Register(Component.For().ServiceOverrides(ServiceOverride.ForKey("Listeners").Eq(typeof(X.Y.Z.DefaultActivityListener).Name)).生活方式.瞬态);Container.Register(Component.For().Named("DefaultActivityListener").生活方式.瞬态);

当我现在尝试解析 XYZActivityService 的实例时,Windsor 在 Castle.MicroKernel.SubSystems.Conversion.ArrayConverter.PerformConversion(String,输入).

PerformConversion 方法的实现是:

公共覆盖对象 PerformConversion(String value, Type targetType){抛出新的 NotImplementedException();}

我应该补充一点,如果我删除 ServiceOverrides 调用,一切都会按预期运行.因此,我连接 Listeners 参数的方式特别有问题.顺便说一下,侦听器是一个属性而不是构造函数参数.

看到 XML 配置按预期工作,我如何最好地使用 fluent API(未实现 PerformConversion 方法)以达到相同的结果?

我使用的是 2.0 版.

编辑

我将问题扩展到您将如何在代码中实现此配置,无论是否使用 fluent API.

更新

如果您尝试将单个元素分配给数组属性,则会出现问题.下面提供了单元测试来说明问题.

命名空间组件{公开课A{公共我[]我{得到;放;}}公共接口 I{字符串名称 { 获取;}}公开课 B : I{公共字符串名称{获取{返回B";} }}公开课 C : I{公共字符串名称{获取{返回C";} }}}[测试方法]公共无效 ArrayPropertyTestApi(){//通行证使用 (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer()){container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName, typeof(Components.C).FullName)));container.Register(Component.For<Components.B>());container.Register(Component.For<Components.C>());Components.A svc = container.Resolve();Assert.IsTrue(svc.I.Length == 2);Assert.IsTrue(svc.I[0].Name == "B");Assert.IsTrue(svc.I[1].Name == "C");}}[测试方法]公共无效 ArrayPropertyTestApi2(){//失败使用 (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer()){container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName)));container.Register(Component.For<Components.B>());container.Register(Component.For<Components.C>());Components.A svc = container.Resolve();//抛出NotImplementedExceptionAssert.IsTrue(svc.I.Length == 1);Assert.IsTrue(svc.I[0].Name == "B");}}

问题仍然存在.

谢谢.

解决方案

[TestFixture]公共类 WindsorTests {[测试]公共无效ArrayConfig(){var container = new WindsorContainer();container.Register(Component.For<Listener>().Named("listener"));container.Register(Component.For().ServiceOverrides(ServiceOverride.ForKey("listeners").Eq(new[] {"listener"})));var service = container.Resolve();Assert.AreEqual(1, service.Listeners.Length);}}公共类监听器 {}公共类活动服务{公共监听器 [] 监听器 { 获取;放;}}

这里的关键部分是 new[] {"listener"}.MicroKernel 需要知道参数 listeners 是一个数组,如果您只传递listener",它会假定参数是标量并抛出,因为它无法将标量转换为数组.>

Given this XML configuration (which works)

<component type="X.Y.Z.ActivityService, X.Y.Z.Services" id="X.Y.Z.ActivityService" lifestyle="transient">
  <parameters>
    <Listeners>
      <array>
        <item>${DefaultActivityListener}</item>
      </array>
    </Listeners>
  </parameters>
</component>

<component type="X.Y.Z.DefaultActivityListener, X.Y.Z.Services" id="DefaultActivityListener" lifestyle="transient" /> 

I have converted to use the fluent API as below (which doesn't work):

Container.Register(
    Component.For<X.Y.Z.ActivityService>()
    .ServiceOverrides(
        ServiceOverride.ForKey("Listeners").Eq(typeof(X.Y.Z.DefaultActivityListener).Name))
    .LifeStyle.Transient
);

Container.Register(
    Component.For<X.Y.Z.DefaultActivityListener>()
    .Named("DefaultActivityListener")
    .LifeStyle.Transient
);

When I now attempt to resolve an instance of X.Y.Z.ActivityService Windsor throws a NotImplementedException in Castle.MicroKernel.SubSystems.Conversion.ArrayConverter.PerformConversion(String, Type).

The implementation of the PerformConversion method is:

public override object PerformConversion(String value, Type targetType)
{
    throw new NotImplementedException();
}

I should add that if I remove the ServiceOverrides call, all behaves as expected. So there is specifically something wrong in the way I am wiring up the Listeners parameter. Listeners by the way is a property as opposed to a constructor parameter.

Seeing as the XML config works as expected how do I best use the fluent API (short of implementing the PerformConversion method) in order to achieve the same result?

I am using Release 2.0.

EDIT

I will extend the question to how would you achieve this configuration in code, with or without use of the fluent API.

UPDATE

It appears the problem occurs if you attempt to assign a single element to an array property. Unit tests provided below to illustrate issue.

namespace Components
{
    public class A
    {
        public I[] I { get; set; }
    }

    public interface I
    {
        string Name { get; }
    }

    public class B : I
    {
        public string Name { get { return "B"; } }
    }

    public class C : I
    {
        public string Name { get { return "C"; } }
    }
}


[TestMethod]
public void ArrayPropertyTestApi()
{
    //PASSES
    using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
    {
        container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName, typeof(Components.C).FullName)));
        container.Register(Component.For<Components.B>());
        container.Register(Component.For<Components.C>());

        Components.A svc = container.Resolve<Components.A>();
        Assert.IsTrue(svc.I.Length == 2);
        Assert.IsTrue(svc.I[0].Name == "B");
        Assert.IsTrue(svc.I[1].Name == "C");
    }
}

[TestMethod]
public void ArrayPropertyTestApi2()
{
    //FAILS
    using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
    {
        container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName)));
        container.Register(Component.For<Components.B>());
        container.Register(Component.For<Components.C>());

        Components.A svc = container.Resolve<Components.A>(); //Throws NotImplementedException
        Assert.IsTrue(svc.I.Length == 1);
        Assert.IsTrue(svc.I[0].Name == "B");
    }
}

Question still stands.

Thanks.

解决方案

[TestFixture]
public class WindsorTests {

    [Test]
    public void ArrayConfig() {
        var container = new WindsorContainer();
        container.Register(Component.For<Listener>().Named("listener"));
        container.Register(Component.For<ActivityService>()
            .ServiceOverrides(ServiceOverride.ForKey("listeners").Eq(new[] {"listener"})));
        var service = container.Resolve<ActivityService>();
        Assert.AreEqual(1, service.Listeners.Length);
    }
}

public class Listener {}

public class ActivityService {
    public Listener[] Listeners { get; set; }
}

The key part here is the new[] {"listener"}. The MicroKernel needs to know that the parameter listeners is an array, if you pass just "listener" it assumes that the parameter is scalar and throws because it can't convert a scalar to an array.

这篇关于Castle Windsor Fluent API:将具有单个项的数组定义为依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆