使用 Simple Injector 和 IHttpControllerActivator 解决 ASP.NET Web API 中的依赖关系

Resolve dependencies in ASP.NET Web API with Simple Injector and IHttpControllerActivator

我目前正在使用 Simple Injector 将依赖项解析到我的 Asp.Net Web Api 项目中。

从文档中你可以这样配置它:

protected void Application_Start() {
    // Create the container as usual.
    var container = new Container();
    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

    // Register your types, for instance using the scoped lifestyle:
    container.Register<IUserRepository, SqlUserRepository>(Lifestyle.Scoped);

    // This is an extension method from the integration package.
    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    container.Verify();

    GlobalConfiguration.Configuration.DependencyResolver =
        new SimpleInjectorWebApiDependencyResolver(container);

    // Here your usual Web API configuration stuff.
}

这里的要点是注册 Web Api 控制器和设置自定义依赖解析器。

但是我刚刚阅读了 Mark Seemann 关于如何在 Asp.Net Web Api 中配置依赖注入的文章 Api:

从这些文章中,我了解到有比实施 IDependencyResolver 更好的选择来解决 Web Api 依赖项。 另一个选项是创建 IHttpControllerActivator 的实现,作为 IoC 容器上的适配器。

这是我使用 SimpleInjector 编写的实现:

public class SimpleInjectorControllerActivator : IHttpControllerActivator
{
    private readonly Container _container;

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

    public IHttpController Create(HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        request.RegisterForDispose(_container.BeginExecutionContextScope());

        return (IHttpController)_container.GetInstance(controllerType);
    }
}

并且在 Application_Start 方法中,我替换了这一行:

GlobalConfiguration.Configuration.DependencyResolver =
    new SimpleInjectorWebApiDependencyResolver(container);

通过这一行:

GlobalConfiguration.Configuration.Services.Replace(
    typeof(IHttpControllerActivator),
    new SimpleInjectorControllerActivator(container));

我想知道 IHttpControllerActivator 的实施是否有效,以及这种方法是否有效并且是否与正常方法一样有效?

是的,您的实施有效。

只是注意不要在同一应用程序中同时使用 SimpleInjectorWebApiDependencyResolverSimpleInjectorControllerActivator。两者都启动一个 ExecutionContextScope,这可能导致在同一个 Web 请求中有两个范围,因此它们是互斥的。

在依赖解析器上使用控制器激活器的一个普遍优势是,当无法创建服务时,依赖解析器契约会强制适配器 return null。这是开发人员 运行 遇到的一个非常普遍的问题,它经常导致令人困惑的 controller does not have a default constructor 异常。使用 IHttpControllerActivator 时不存在此问题,因为合同强制您 return 一个值或抛出异常。

然而,

Simple Injector Web API 集成项目通过从不 returning null(而是抛出异常)来防止依赖解析器出现此问题,以防请求的服务是一个 API 控制器(因此隐含地违反了 IDependencyResolver 的合同)。

使用 SimpleInjectorDependencyResolver 的一个优点是创建在执行上下文范围内运行的消息处理程序变得更加容易,因为您可以通过调用 request.GetDependencyScope() 方法触发此范围的创建。在当前的实现中,范围在创建控制器时才开始,这是在您 运行 处理程序之后。改变它并不难,但涉及到改变你的控制器激活器并有一个最外层的处理程序来启动执行上下文范围(或者再次回退到管理执行上下文范围的依赖解析器)。

Mark Seemann 的一个论点是,它变得很难传递上下文,这是一个非常有效的观点,只要您的 components don't require this context during construction. But this is not a problem you'll experience when using Simple Injector, because there is an extension method 可以帮助您访问 HttpRequestMessage。因此,尽管 IDependencyResolver 抽象不是为获取上下文信息而设计的,但有一些方法可以获取此上下文信息。

过去我们决定为 IDependencyResolver 使用适配器,主要是因为这是所有 DI 容器的常见做法。我对这个决定有些遗憾,但使用 SimpleInjectorDependencyResolver 现在通常是将 Simple Injector 插入 Web API 的最简单方法。我们也考虑过添加 SimpleInjectorControllerActivator,但这对大多数用户没有实际好处,同时我们仍然必须记录何时使用什么。所以我们决定坚持使用依赖解析器适配器;如您所见,激活器的适配器很容易为任何需要它的人创建。

然而,对于 ASP.NET Core,我们进入了不同的方向,正如您在 the documentation 中所见,集成包实际上包含开箱即用的 SimpleInjectorControllerActivator。在 ASP.NET Core 中,控制器激活器是完美的拦截点,并且由于类似于 OWIN 的管道,范围可以轻松地包裹在请求周围。因此,对于 ASP.NET Core,建议的做法是使用控制器激活器作为拦截点。