Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Windsor: inject IEnumerable<IService> using only a subset of registered components for IService

Consider the following scenario of services and components in a sample C# console application

public interface IService { }
public class FooService: IService { }
public class BarService: IService { }
public class BuzzService: IService { }
public class AwesomeService: IService { }

public class Consumer 
{
    public Consumer(IEnumerable<IService> services)
    {
        // do some initilization work here...
    }
}

public class AnotherConsumer 
{
    public AnotherConsumer(IEnumerable<IService> services)
    {
        // do some initilization work here...
    }
}

Let's imagine to do the following registrations inside the composition root:

var container = new WindsorContainer();

container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel, true));

container.Register(Component.For<IService>().ImplementedBy<FooService>());
container.Register(Component.For<IService>().ImplementedBy<BarService>());
container.Register(Component.For<IService>().ImplementedBy<BuzzService>());
container.Register(Component.For<IService>().ImplementedBy<AwesomeService>());

container.Register(Component.For<Consumer>());
container.Register(Component.For<AnotherConsumer>());

// consumer got injected all 4 different implementations of IService
// due to CollectionResolver
var consumer = container.Resolve<Consumer>();

// anotherConsumer got injected all 4 different implementations of 
// IService due to CollectionResolver
var anotherConsumer = container.Resolve<AnotherConsumer>(); 

This kind of scenario works fine and I did so several times.

What if, for some reason, I would like to inject inside the constructor of Consumer class only two different implementations of IService, for instance only FooService and BarService (while still continuing to inject all the available implementations of IService inside the constructor of AnotherConsumer) ?

Is there an elegant way to do so?

like image 531
Enrico Massone Avatar asked Oct 08 '18 12:10

Enrico Massone


People also ask

What chapter is dependency injection in Castle Windsor?

Chapter 10. Castle Windsor - Dependency Injection in .NET Chapter 10. Castle Windsor In the previous nine chapters, we discussed patterns and principles that apply to DI in general, but, apart from a few examples, we have yet to take a detailed look at how to apply them using any particular DI C ONTAINER.

Can I use IEnumerable instead of IList<iservice> to register my dependencies?

Show activity on this post. As of ASP.NET Core 2.0, if you inject your dependencies as IEnumerable<IService> instead of IList<IService>, you can forgo registering the list itself, leaving you with just the individual services registration. amazing! Where can I find related documents?

How do I register a container in Castle Windsor?

With Castle Windsor the order of the registrations enables this behavior, so the first implementation will be injected into the decorator. // Initialize the container var container = new WindsorContainer (); // Register the default implementation container. Register ( Component. For < IRepository < Customer > () .

Is Castle Windsor a di/IoC container?

The same principles would apply in a GUI application using DI/IoC. Castle Windsor is a third-party IoC container, meaning that it is not part of the .NET framework itself. We will be using the NuGet Package Manager to install and reference the Castle components:


2 Answers

I grabbed my copy of the first edition of Dependency Injection Principles, Practices, and Patterns. It contains a complete chapter on Castle Windsor and discusses this exact scenario. The trick is to do two things:

  • Define the collection registrations as named registrations using .Named(string)
  • Specify an override for Consumer using .ServiceOverrides(object)

The following code sample is almost straight out of the book (with the names replaced with your examples):

container.Register(Component
    .For<IService>()
    .ImplementedBy<FooService>()
    .Named("Foo"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<BarService>()
    .Named("Bar"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<BuzzService>()
    .Named("Buzz"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<AwesomeService>()
    .Named("Awesome"));

container.Register(Component
    .For<Consumer>()
    .ServiceOverrides(new
        {
            services = new[] { "Foo", "Bar" }
        }));

container.Register(Component.For<AnotherConsumer>());

var consumer = container.Resolve<Consumer>();
like image 134
Steven Avatar answered Oct 07 '22 08:10

Steven


The answer that @Steven gave is very much on point. There's a slightly nicer API to do that (added in version 3 of Windsor, so not covered by the aforementioned book) without having to use .Named or registering all your IServices one by one.

container.Register(Component
    .For<Consumer>()
    .DependsOn(
       Dependency.OnComponentCollection(
          "services", typeof(FooService), typeof(BarService)
       )
    )
);

Or if you don't want to use the dependency name, you can specify it by type

container.Register(Component
    .For<Consumer>()
    .DependsOn(
       Dependency.OnComponentCollection<IEnumerable<IService>>(
          typeof(FooService), typeof(BarService)
       )
    )
);

With that you can just register your IComponents via convention like the following, without any extra fuss.

container.Register(Classes.FromThisAssembly()
    .BasedOn<IService>()
    .WithServiceBase()
    .LifestyleTransient());
like image 3
Krzysztof Kozmic Avatar answered Oct 07 '22 09:10

Krzysztof Kozmic