Search code examples
c#inversion-of-controlcastle-windsorioc-container

Windsor Ioc Service Overrides: There is a component already registered for the given key


I have been trying to configure Windsor to provide a different implementation for a service depending on which class is being constrcuted:

I have read this http://docs.castleproject.org/Windsor.Registering-components-one-by-one.ashx#Supplying_the_component_for_a_dependency_to_use_Service_override_9

and asked this yesterday Windsor Ioc container: How to register that certain constructors take different implementation of an interface

The answer to that question works correctly when I resolve the class directly, but not when it is in an object graph a few levels deep, and the class I want to override is used as a default implementation of another interface in a different registration

e.g.

I have 2 MVC controllers. One for logging and one for cardpayments. The logging one takes a logging provider which in turn takes an IService. The CardPaymentController takes a card payment provider which in turn takes an IService. The CardPaymentProvider should get a secure service and the logging provider a normal service

code is below:

Registrations:

Component.For<ILoggingProvider>().ImplementedBy<LoggingProvider>(),
Component.For<ICardPaymentProvider>().ImplementedBy<CardPaymentProvider>(),
Component.For<IService>().ImplementedBy<WebService>().Named("default"),
Component.For<IService>().ImplementedBy<SecureWebService>().Named("secure"),
Component.For<CardPaymentProvider>().ServiceOverrides(ServiceOverride.ForKey("service").Eq("secure")),
Component.For<LoggingProvider>().ServiceOverrides(ServiceOverride.ForKey("service").Eq("default"))

Class hierarchy:

 public LoggingController(ILoggingProvider loggingProvider)
 {
            this.loggingProvider = loggingProvider;
 }

 public CardPaymentController(ICardPaymentProvider cardPaymentProvider)
{
            this.cardPaymentProvider = cardPaymentProvider;
}

    public interface IService
    {
        void Doit();
    }
    public class WebService : IService
    {
        public void Doit()
        {
            throw new NotImplementedException();
        }
    }
    public class SecureWebService : IService
    {
        public void Doit()
        {
            throw new NotImplementedException();
        }
    }
    public class CardPaymentProvider : ICardPaymentProvider
    {
        private readonly IService service;
        public CardPaymentProvider(IService service)
        {
            this.service = service;
        }
    }
    public interface ICardPaymentProvider
    {
    }
    public class LoggingProvider : ILoggingProvider
    {
        private readonly IService service;
        public LoggingProvider(IService service)
        {
            this.service = service;
        }
    }
    public interface ILoggingProvider
    {
    }

This produces an error on start up:

"There is a component already registered for the given key Spike.CardPaymentProvider"

If I add Named("somename") to either the CardPaymentProvider registration or the ICardPaymentProvider registration, then it starts OK, but doesn't provide a secure implementation of the service to the CardPaymentProvider - just a normal version.

What am I doing wrong?


Solution

  • You have to define the service overrides in the same registration. Instead of:

    Component.For<ICardPaymentProvider>().ImplementedBy<CardPaymentProvider>(),
    Component.For<CardPaymentProvider>().ServiceOverrides(ServiceOverride.ForKey("service").Eq("secure")),
    

    do:

    Component.For<ICardPaymentProvider>()
             .ImplementedBy<CardPaymentProvider>()
             .ServiceOverrides(ServiceOverride.ForKey("service").Eq("secure")),