Search code examples
c#asp.netinversion-of-controlcastle-windsor

Is it possible to override a named registration in Castle Windsor 3?


I have two interfaces, IExampleClient and IServiceUsingClient. Each has two implementations. They are registered in a Castle Windsor installer as follows:

        container.Register(Component.For<IExampleClient>()
            .ImplementedBy<FirstClient>()
            .LifestyleTransient()
            .Named("FirstClient"));

        container.Register(Component.For<IExampleClient>()
            .ImplementedBy<SecondClient>()
            .LifestyleTransient()
            .Named("SecondClient"));

        container.Register(Component.For<IServiceUsingClient>()
            .ImplementedBy<FirstService>()
            .LifestyleTransient()
            .Named("FirstService")
            .DependsOn(Dependency.OnComponent(typeof(IExampleClient), "FirstClient")));

        container.Register(Component.For<IServiceUsingClient>()
            .ImplementedBy<SecondService>()
            .LifestyleTransient()
            .Named("SecondService")
            .DependsOn(Dependency.OnComponent(typeof(IExampleClient), "SecondClient")));

See that both implementations of IServiceUsingClient depend on a different implementation of IExampleClient. To make this work I've used the DependsOn method to ensure a specific named registration is used.

I now have a set of component tests in which I want to mock the IExampleClient implementations. Normally I'd do this by overriding the registration using the IsDefault() option. However, because these are named registrations I can't do this. Castle complains that there's already a registration with the supplied name. However, if I register with a different name then my IServiceUsingClient will get my real implementations, not my mocks.

I'm hoping that I'm overlooking a technique that will achieve my goal without me having to override the entire dependency chain in my tests. Any ideas?


Solution

  • You can override a named registration using an IHandlerSelector. Here's an example. In the unit test you'll see that I'm registering two named implementations for a single dependency (IGreetingProvider- why? IDK.)

    I'm then indicating that when another class needs an instance of IGreetingProvider, the instance named "Hello" should be used.

    But then I'm registering an IHandlerProvider which intercepts that request and, out of the available registered components, selects the one named "Goodbye" instead.

    So in effect this allows me to override what was previously registered with the container.

    [TestClass]
    public class HandlerSelectorTests
    {
        [TestMethod]
        public void HandlerSelectorOverridesRegistration()
        {
            using (var container = new WindsorContainer())
            {
                container.Register(Component.For<IGreetingProvider, Hello>()
                    .Named("Hello"));
                container.Register(Component.For<IGreetingProvider, Goodbye>()
                    .Named("Goodbye"));
                container.Register(Component.For<SaysSomething>()
                    .DependsOn(Dependency.OnComponent(typeof(IGreetingProvider), "Hello")));
                var handlerSelector = new MyHandlerSelector();
                container.Kernel.AddHandlerSelector(handlerSelector);
                var resolved = container.Resolve<SaysSomething>();
                Assert.AreEqual("Goodbye", resolved.SaySomething());
            }
        }
    }
    
    public class MyHandlerSelector : IHandlerSelector
    {
        public bool HasOpinionAbout(string key, Type service)
        {
            return key == "Hello";
        }
    
        public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
        {
            return handlers.First(handler => handler.ComponentModel.Name == "Goodbye");
        }
    }
    
    public class SaysSomething
    {
        private readonly IGreetingProvider _greetingProvider;
    
        public SaysSomething(IGreetingProvider greetingProvider)
        {
            _greetingProvider = greetingProvider;
        }
    
        public string SaySomething()
        {
            return _greetingProvider.GetGreeting();
        }
    }
    
    public interface IGreetingProvider
    {
        string GetGreeting();
    }
    
    public class Hello : IGreetingProvider
    {
        public string GetGreeting()
        {
            return "Hello";
        }
    }
    
    public class Goodbye : IGreetingProvider
    {
        public string GetGreeting()
        {
            return "Goodbye"; //Ok, it's not a greeting.
        }
    }