简体   繁体   中英

Castle Windsor multiple implementations of interface

I have the following installer but for some odd reason it is not resolving correctly. I have an interface where there are 2 implementations of it but want to inject the correct instance based on naming conventions .

I am expecting in this instance that the correct instance of ICommand will be injected based on how they are named. However, for some odd reason both controllers are picking the very first instance, ie FooCommand due to it being defined first in the installer.

Not sure what I have done wrong? Perhaps, is there an alternative way of doing this?

public interface ICommand { }

public class FooCommand : ICommand { }

public class BarCommand : ICommand { }

public class SomeController : ApiController
{
    public SomeController(ICommand fooCommand) { }
}

public class HelloController : ApiController
{
    public HelloController(ICommand barCommand) { }
}

container.Register(
    Component.For<ICommand>()
        .Named("fooCommand")
        .ImplementedBy<FooCommand>()
        .LifestyleSingleton(),
    Component.For<ICommand>()
        .Named("barCommand")
        .ImplementedBy<BarCommand>()
        .LifestyleSingleton());

Like @steven said, it's generally not a good idea and if not managed properly may lead to discoverability issues down the line, but assuming you know what you're doing you can build a IContributeComponentModelConstruction that will match constructor parameters of type ICommand on your controllers with Windsor components having the same name.

public class ControllerCommandMatcher : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        // or whatever other condition to bail out quickly
        if (model.Implementation.Name.EndsWith("Controller") == false) return;

        foreach (var constructor in model.Constructors)
        {
            foreach (var dependency in constructor.Dependencies)
            {
                if (dependency.TargetItemType != typeof (ICommand)) continue;
                dependency.Parameter = new ParameterModel(dependency.DependencyKey,
                    ReferenceExpressionUtil.BuildReference(dependency.DependencyKey));
            }
        }
    }
}

The tricky bit is this:

new ParameterModel(dependency.DependencyKey,
       ReferenceExpressionUtil.BuildReference(dependency.DependencyKey))

It basically tells Windsor that the dependency (the constructor parameter), for example fooCommand should be satisfied with a component of the same name ( fooCommand ).

Then add your contributor to the container

container.Kernel.ComponentModelBuilder.AddContributor(new ControllerCommandMatcher());

Here's the documentation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM