views:

993

answers:

1

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.

+2  A: 
[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.

Mauricio Scheffer
@mausch - Thats it, Thanks. Is there documentation around this that I missed, if so can you point me in the right direction as I have another question I need answering, will post on SO shortly.
crowleym
The fluent API is documented here: http://using.castleproject.org/display/IoC/Fluent+Registration+API it's rather scarce at the moment but feel free to post here or the google group (http://groups.google.com/group/castle-project-users)
Mauricio Scheffer