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

Bri*_*thy 5 c# asp.net unity-container asp.net-web-api

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

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

这可能吗?

public class AuthenticateController : ApiController
{
    public AuthenticateController(IAuthenticationService authenticationService)
    {
    }
Run Code Online (Sandbox Code Playgroud)

dan*_*wig 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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));
    }
});
Run Code Online (Sandbox Code Playgroud)

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

如果服务本身需要注入怎么办?

在根组成期间:

container.Register<HttpGetAuthenticationService>();
container.Register<HttpPostAuthenticationService>();
Run Code Online (Sandbox Code Playgroud)

提供者实施:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

...这又不是 Unity 的代码,但我希望 Unity 可以做同样的事情,即使 API 不同。我同意@Maarten 的观点,这种事情可以放在组合根中,也可以放在应用程序级提供程序中。我只是倾向于后者而不是前者,可能是因为它对我来说似乎不那么“神奇”。