Search code examples
asp.net-web-apidryioc

Inject service with different configuration into controller


In a Web API application, I have two controllers, MyAController and MyBController, each depending on IMyService but with a different configuration:

public class MyAController : ApiController
{
    private readonly IMyService service;
    public MyAController(IMyService service)
    {
        this.service = service;
    }
}

public class MyBController : ApiController
{
    private readonly IMyService service;
    public MyBController(IMyService service)
    {
        this.service = service;
    }
}

public interface IMyService
{
}

public class MyService : IMyService
{
    private readonly string configuration;
    public MyService(string configuration)
    {
        this.configuration = configuration;
    }
}

I've tried configuring DryIoc the following way:

private enum ServiceKeyEnum
{
    ServiceA,
    ServiceB
}

container.RegisterInstance("configurationA", serviceKey: "CONFIGURATIONA");
container.RegisterInstance("configurationB", serviceKey: "CONFIGURATIONB");
container.Register<IMyService, MyService>(Reuse.Singleton, Made.Of(() => new MyService(Arg.Of<string>("CONFIGURATIONA"))), serviceKey: ServiceKeyEnum.ServiceA);
container.Register<IMyService, MyService>(Reuse.Singleton, Made.Of(() => new MyService(Arg.Of<string>("CONFIGURATIONB"))), serviceKey: ServiceKeyEnum.ServiceB);

container.Register<MyAController>(Reuse.InResolutionScope, made: Parameters.Of.Details((r, p) => ServiceDetails.IfUnresolvedReturnDefault).Type<IMyService>(serviceKey: ServiceKeyEnum.ServiceA));
container.Register<MyBController>(Reuse.InResolutionScope, made: Parameters.Of.Details((r, p) => ServiceDetails.IfUnresolvedReturnDefault).Type<IMyService>(serviceKey: ServiceKeyEnum.ServiceB));

and if I try to call resolve using:

var controllerA = container.Resolve<MyAController>();
var controllerB = container.Resolve<MyBController>();

I get two controllers configured with configurationA and configurationB respectively. However, when I try to call the api using a REST call, I get the following error:

An error occurred when trying to create a controller of type 'MyAController'. Make sure that the controller has a parameterless public constructor.

so I guess, that I need to register the controller in a different way... but how?

Any help would be greatly appreciated....


Solution

  • The error is caused by improper setup for controllers. The DryIoc.WebApi extension have already discovered and registered your controllers, so normally you don't need to do it yourself. I will provide the working code (from the question comments) for you specific setup later. But now the reason behind the "parameterless constructor..": when DryIoc fails, WebAPI falls back to using Activator.CreateInstance for controller, which expects parameterless constructor. The fallback masks the original DryIoc error. To find it, you can setup DryIoc.WebApi extension as:

    container = container.WithWebApi(throwIfUnresolved: type => type.IsController());
    

    The working setup for your case, which registers dependencies with condition to select controller for injection:

    container.Register<IMyService, MyService>(Made.Of(
        () => new MyService(Arg.Index<string>(0)), _ => "configurationA"),  
        Reuse.Singleton, 
        setup: Setup.With(condition:  r => r.Parent.ImplementationType == typeof(MyAController)));
    
    container.Register<IMyService, MyService>(Made.Of(
        () => new MyService(Arg.Index<string>(0)), _ => "configurationB"),  
        Reuse.Singleton, 
        setup: Setup.With(condition:  r => r.Parent.ImplementationType == typeof(MyBController)));
    

    The main thing that this setup does not require special controller registration.

    Plus you can avoid using service keys, and no need to register config strings separately.