Search code examples
c#dependency-injectioncastle-windsor

Castle Windsor Dependency Injection with custom implementation based on customer


We have a situation where we are using Castle Windsor dependency injection to inject an IService into a wep api controller like so:

public FooController : ApiController
{
    private IService _service;

    public FooController(IService service)
    {
        _service = service;
    }
}

And registering the service like this:

 container.Register(Component.For<IService>().ImplementedBy<Service>().LifestyleTransient());

Where service is something like:

public Service : IService
{
    public virtual string Logic()
    {
        return "Service Logic";
    }
}

The problem is, some customers have semi different business logic then what the base has, so we need to use another implementation of Service that does what that customer needs.

So if there is 2 customers (Customer1 and Customer2), Customer1 should use the default Service, but Customer2 should use a custom implementation called Customer2Service which will inherit from Service and override as needed like so:

public Customer2Service : Service
{
    public override string Logic()
    {
        var response = base.Logic();

        return "Customer 2 Logic " + response;
    }
}

Questions

  1. First off, does this seem like the right architecture to solve this particular problem of customer specific code?
  2. Is there a way to solve this using dependency injection? (we don't want a giant switch statement that just chooses which service to use based on the customer name in each action)

What we've tried

We tried to change the dependency injection to be property based like so:

public FooController : ApiController
{
    private IService _service;

    public FooController()
    {
    }

    public HttpResponseMessage FooAction(string customer)
    {
        _service = container.Resolve<IService>(customer);

        ...
    }
}

container.Register(Component.For<IService>().ImplementedBy<Service>().LifestyleTransient());
container.Register(Component.For<IService>().ImplementedBy<Customer2Service>().LifestyleTransient().Named("Customer2"));

The problem with this is, we have to have a custom service for each customer even if they didn't need it which can get unwieldy. Otherwise the resolve would throw an exception saying that named dependency doesn't exist.


Solution

  • You can use a factory here:

    class ServiceFactory : IServiceFactory
    {
        public IService GetService(string customer)
        {
            switch (customer)
            {
                case "special":
                    return container.Resolve<IService>("special");
                default:
                    return container.Resolve<IService>("default");
            }
        }
    }
    

    Then you can inject the factory into your controllers and get an IService from the factory. In the switch statement you only declare the special cases, all the other ones fo to the default clause.