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

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

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

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

这可能吗?

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

简短的回答是有可能,但我不推荐这样做,因为 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 或任何具有数据的对象,您需要决定哪个实现 return。然后,您使用 unity 注册提供者,构造函数将其注入到您的控制器中。最后在操作中,您使用提供程序来获取正确的身份验证服务实现。

如果您真的想避免使用 provider/factory 模式,老实说,我不知道在 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 的观点,这种事情可以在组合根或应用程序级提供程序中进行。我只是更喜欢后者而不是前者,可能是因为它对我来说似乎不那么“神奇”。