0
votes

With Castle Windsor 3.2 I need to resolve service implementation on the fly depending on contextual parameters. Here is basic example of what I want to do:

I have a service IFoo

public interface IFoo
{
   object GetSomething();
}

Implemented by 2 different classes Foo1 and Foo2

public class FooBase
{
   public int Number {get; private set;}

   public FooBase(int number)
   {
      Number = number;
   }
}

public class Foo1 : IFoo
{
   public Foo1(int number):base(number)
   public object GetSomething()
   {
      return "Foo1";
   }
}

public class Foo2 : IFoo
{
   public Foo2(int number):base(number)
   public object GetSomething()
   {
      return "Foo2";
   }
}

I want to call the castle windsor resolve method with number as parameter, and depending on the number I want to have Foo1 or Foo2 object:

var foo1 = container.Resolve<IFoo>(new {number= 1});
// foo1 is Foo1
var foo2 = container.Resolve<IFoo>(new {number= 2});
// foo2 is Foo2

I tried several potential solutions, but I'm realy confused because of the lack of Castle Windsor documentation and examples, adding to that most of the examples are not up to date and are using obsolete API.

So I have seen some example using the following concepts, but none of them seemed to much my requirement :

  1. HandlerSelector
  2. Typed Factory facility
  3. UsingFactoryMethod
2
Can you provide more information about your scenario? What supplies the value of the "number" at runtime? I'm guessing there's some context object somewhere that houses this information? There might be a way to do this with type factories, etc. but this smells like a common pattern I use HandlerSelectors for on a regular basis, but the owner of "number" matters. You might be able to restructure your desired solution a bit and get a much cleaner outcome.kellyb

2 Answers

1
votes

Here is the code that worked for me:

container = new WindsorContainer();
        container.AddFacility<TypedFactoryFacility>();
        container.Register(
            Component.For<IFoo>().ImplementedBy<Foo1>().Named("Foo1"),
            Component.For<IFoo>().ImplementedBy<Foo2>().Named("Foo2"),
            Component.For<IFooFactory>().AsFactory(c => c.SelectedWith<FooSelector>())
        );

Create a Foo factory :

public interface IFooFactory
{
    IFoo CreateFoo(int number);
}

and a selector:

public class FooSelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(System.Reflection.MethodInfo method, object[] arguments)
    {
        var argument = (int) arguments[0];
        return string.Format("Foo{0}", argument);
    }
}
1
votes

use the following registration code:

container = new WindsorContainer();
        container.AddFacility<TypedFactoryFacility>();
        container.Register(
            Component.For<FooSelector>,
            Component.For<Foo1>(),
            Component.For<Foo2>(),
            Component.For<IFooFactory>().AsFactory(c => c.SelectedWith<FooSelector>())
        );

Create a Foo factory :

public interface IFooFactory
{
    IFoo CreateFoo(int number);
}

and a selector:

public class FooSelector : DefaultTypedFactoryComponentSelector
{
    protected override Type GetComponentType(System.Reflection.MethodInfo method, object[] arguments)
    {
        var argument = (int) arguments[0];
        if (argument == 1) return typeof (Foo1);
        if (argument == 2) return typeof(Foo2);
        throw new ApplicationException();
    }
}

Now resolve the IFooFactory and call the create methode with the value that you want.

Kind regards, Marwijn.