如何使用Unity基于http请求参数动态地将服务注入到asp.net web api控制器中

时间:2015-05-08 12:38:48

标签: c# asp.net asp.net-web-api unity-container

我正在使用Unity将服务实例注入到我的ASP.NET Web API控制器的构造函数中。

在下面的代码中,我想根据发出的http请求注入不同的IAuthenticationService实现。

这可能吗?

public class AuthenticateController : ApiController
{
    public AuthenticateController(IAuthenticationService authenticationService)
    {
    }

1 个答案:

答案 0 :(得分:4)

简短的回答是它是可能的,但我不推荐它,因为IoC容器必须静态地使用HttpContext.Current来完成它。我推荐的是更像这样的模式:

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);
    }
}

提供程序方法arg不必是字符串,它可以是HttpContextBase或任何具有您需要的数据的对象来决定返回哪个实现。然后使用unity注册提供程序,构造函数将其注入控制器。最后,在操作中,您使用提供程序来获取正确的身份验证服务实现。

如果你真的想避开提供商/工厂模式,老实说我不知道​​Unity中会是什么样子。但是在SimpleInjector(另一个基本上与Unity相同的IoC库)中,它看起来像这样:

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));
    }
});

虽然上面应该有效(并且应该与Unity一样),但它只能通过使用静态HttpContext.Current对象来实现。我通常不喜欢这种方法,因为它隐藏了组合根中的知识,并且有效地做了与提供者相同的事情。但这只是我的观点,你可以自由选择。

如果服务需要自己注射怎么办?

根组合期间:

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

提供商实施:

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));
        }
    }
}

...再次这不是Unity的代码,但我希望Unity可以做同样的事情,即使API不同。我同意@Maarten这种事情可以在组合根或应用程序级提供程序中进行。我倾向于选择后者而不是前者,可能是因为它对我来说似乎不太“神奇”。