Castle Windsor: How do I inject all implementations of interface into a ctor?
Asked Answered
S

4

29

I've written an interface which is implemented by multiple classes. I want to write a Service class which will have all the registered implementations injected into its ctor.

The only solution I can think of is to call the Service Locator within the ctor and ask it to Resolve() all implementations.

Ideally I would like something like this -

interface IVehicle
{
    void Start();
}

class Car : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Car started.");
    }
}

class Truck : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Truck started.");
    }
}

class Motorbike : IVehicle
{
    public void Start()
    {
        Console.WriteLine("Motorbike started.");
    }
}

class VehicleService
{
    // How do I inject all implementations of IVehicle?
    public VehicleService(IEnumerable<IVehicle> vehicles)
    {
        foreach (var vehicle in vehicles)
        {
            vehicle.Start();
        }
    }
}

EDIT - I should mention I'm using Castle Windsor.

Staford answered 4/5, 2012 at 15:53 Comment(0)
V
35

You have to use CollectionResolver. Check this Castle Windsor FAQ:

Windsor, by default when you have dependency on IFoo[], IEnumerable or IList will check if you have a component registered for that exact type (array or list of IFoo), not if you have any components registered for IFoo (array of components, is not the same as a component which is an array). You can change the behavior to say "When you see array or list of IFoo just give me all IFoos you can get" you use CollectionResolver.

Direct link to Castle Resolvers: Resolvers.

Voyeurism answered 4/5, 2012 at 16:0 Comment(0)
K
33

I know this has already been answered, but I thought an example of how to add the CollectionResolver would be useful, so here it is.

Call AddSubResolver before registering the components in the container, e.g.

container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

Register the components as normal:

container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);
Karaite answered 14/3, 2015 at 22:1 Comment(1)
Great example. In case anyone gets the "Handler for System.Collections.Generic.List`1[IFoobar] was not found."} error message, this could be caused by a constructor requiring an implemented ICollection instance, such as public FoobarConstructor(List<IFoobar> ifoobarImplementations), but should be public FoobarConstructor(IEnumerable<IFoobar> ifoobarImplementations)William
L
22

Found this answer useful though I was still missing how to register all the implementations of a given interface.

Hope this helps!

container.Register(
            Classes.FromAssemblyContaining<IVehicle>()
                .BasedOn<IVehicle>().WithService.FromInterface()
            );
Limitary answered 16/12, 2015 at 17:30 Comment(5)
This simply cannot be the accepted answer because it's solving something different but it's very helpful. In combination with the accepted answer it's a clean solution.Money
This doesn't work for me. After registering VehicleService with container.Register(Component.For<VehicleService>().ImplementedBy<VehicleService>()), I replace manual registration (Component.For<IVehicle>().ImplementedBy<Car>()) with Classes.FromAssemblyContaining<IVehicle>() .BasedOn<IVehicle>().WithService.FromInterface(), which causes an exception when I do container.Resolve<VehicleService>(): "Blah blah blah... 'WindsorTest.Program+VehicleService' is waiting for the following dependencies: - Service '....IEnumerable`1[[...]]' which was not registered."Coronary
I posted this up nearly 5 years ago; the project has been through 2 major versions since then. Give this example a go with version 3.3.0 and see if it works; this should tell you if its an issue on your side or a breaking change in Castle Windsor.Limitary
Also check the answer below - are you adding the sub resolver line? container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));Limitary
Yes, I'm adding CollectionResolver before registering. I found the problem: It only works if the classes are marked public (in Windsor 5.0.1) (note that OP's classes weren't public)Coronary
C
4

The answers here are all correct but I just wanted to add a little extra wrinkle to it: You have to add the sub-resolver BEFORE you register your components.

This will work:

container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);

This will NOT work

container = new WindsorContainer();
container.Register(
    Component.For<IVehicle>().ImplementedBy<Car>(),
    Component.For<IVehicle>().ImplementedBy<Truck>(),
    Component.For<IVehicle>().ImplementedBy<Motorbike>()
);
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
Cacie answered 27/9, 2016 at 14:26 Comment(2)
Registering the interface implementations (re: CountZero) should also be done after the AddSubResolver call.Gravettian
How to define CollectionResolver, when registration done in configuration xml file?Danby

© 2022 - 2024 — McMap. All rights reserved.