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)
{
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With