Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically inject service into asp.net web api controller based on http request parameter using Unity

I am using Unity to inject an instance of a service into the constructor of my ASP.NET Web API controller.

In the below code, I want to inject a different implementation of IAuthenticationService based on the http request made.

Is this possible?

public class AuthenticateController : ApiController
{
    public AuthenticateController(IAuthenticationService authenticationService)
    {
    }
like image 762
Brian McCarthy Avatar asked Sep 29 '22 00:09

Brian McCarthy


1 Answers

The short answer is that it's possible, but I would not recommend it, because the IoC container would have to use HttpContext.Current statically to do it. What I recommend is a pattern more like this:

public interface IProvideAuthenticationService
{
    IAuthenticationService GetService(string requestMethod);
}

public class AuthenticationServiceProvider : IProvideAuthenticationService
{
    public IAuthenticationService GetService(string requestMethod)
    {
        switch (requestMethod)
        {
            case "GET":
                return new HttpGetAuthenticationService();
            case "POST":
                return new HttpPostAuthenticationService();
            default:
                throw new NotSupportedException(string.Format(
                    "Cannot find AuthenticationService for requestMethod '{0}'",
                        requestMethod));
        }
    }
}

public class AuthenticateController : ApiController
{
    private readonly IProvideAuthenticationService _authenticationServiceProvider;

    public AuthenticateController(IProvideAuthenticationService authenticationServiceProvider)
    {
        _authenticationServiceProvider = authenticationServiceProvider;
    }

    [HttpGet]
    public ActionResult Get()
    {
        IAuthenticationService authService = _authenticationServiceProvider.GetService(HttpContext.Request.HttpMethod);
    }

    [HttpPost]
    public ActionResult Post()
    {
        IAuthenticationService authService = _authenticationServiceProvider.GetService(HttpContext.Request.HttpMethod);
    }
}

The provider method arg doesn't have to be a string, it can be an HttpContextBase or whatever object has the data you need to decide which implementation to return. You then register the provider with unity and constructor inject it into your controller(s). Finally in the actions, you use the provider to obtain the correct authentication service implementation.

If you really wanted to avoid the provider/factory pattern, I honestly don't know what that would look like in Unity. But in SimpleInjector (another IoC library that basically does the same things as Unity), it would look something like this:

container.Register<IAuthenticationService>(() => {
    string requestMethod = HttpContext.Current.Request.HttpMethod;
    switch (requestMethod)
    {
        case "GET":
            return new HttpGetAuthenticationService();
        case "POST":
            return new HttpPostAuthenticationService();
        default:
            throw new NotSupportedException(string.Format("Cannot find AuthenticationService for requestMethod '{0}'", requestMethod));
    }
});

Though the above should work (and there should be a similar way to do it with Unity), it can only do so by using the static HttpContext.Current object. I generally don't like this approach because it hides knowledge in the composition root, and effectively does the same thing that a provider would. But that's just my opinion, you are free to choose either.

And what if the services need injections themselves?

During root composition:

container.Register<HttpGetAuthenticationService>();
container.Register<HttpPostAuthenticationService>();

Provider implementation:

public class AuthenticationServiceProvider : IProvideAuthenticationService
{
    private readonly Container _container;

    public AuthenticationServiceProvider(Container container)
    {
        _container = container;
    }

    public IAuthenticationService GetService(string requestMethod)
    {
        switch (requestMethod)
        {
            case "GET":
                return _container.GetInstance<HttpGetAuthenticationService>();
            case "POST":
                return _container.GetInstance<HttpPostAuthenticationService>();
            default:
                throw new NotSupportedException(string.Format(
                    "Cannot find AuthenticationService for requestMethod '{0}'",
                        requestMethod));
        }
    }
}

...again this is not code for Unity, but I would expect Unity can do the same thing, even if the API is different. I agree with @Maarten that this kind of thing can go either in the composition root or in an application-level provider. I just tend to prefer the latter over the former, probably because it seems less "magical" to me.

like image 53
danludwig Avatar answered Nov 15 '22 07:11

danludwig