MVC5 Web API 和依赖注入

2024-01-02

尝试在不使用第三方工具的情况下在 Web API 2 上进行一些 DI。
所以,从一些例子中我得到了自定义依赖解析器(为什么没有集成的?奇怪,甚至Microsoft.Extensions.DependencyInjection不提供任何内容):

public class DependencyResolver : IDependencyResolver
    {
        protected IServiceProvider _serviceProvider;

        public DependencyResolver(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
        }

        public IDependencyScope BeginScope()
        {
            return this;
        }

        public void Dispose()
        {

        }

        public object GetService(Type serviceType)
        {
            return this._serviceProvider.GetService(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this._serviceProvider.GetServices(serviceType);
        }

        public void AddService()
        {

        }
    }

然后创建了这个类:

public class ServiceConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var services = new ServiceCollection();
            services.AddScoped<IMyService, MyServiceClient>();

            var resolver = new DependencyResolver(services.BuildServiceProvider());
            config.DependencyResolver = resolver;
        }

    }

并注册它:

protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
            GlobalConfiguration.Configure(ServiceConfig.Register);
        }

但是当我尝试使用它时:

public class TestController : ApiController
    {
        private IMyService _myService = null;

        public TestController(IMyService myService)
        {
            _myService = myService;
        }

        public void Get()
        {
            _myService.DoWork();
        }
}

我收到错误:

尝试创建“TestController”类型的控制器时发生错误。确保控制器具有无参数公共构造函数。

如何以正确的方式煮这个?


你所看到的发生的事情与这个问题 https://stackoverflow.com/a/15910758/264697。简而言之,Web API 会调用它的默认值IHttpControllerActivator请求新控制器实例的实现。该实例将调用您的DependencyResolver.GetService方法。该方法会将调用转发到 MS.DI 的GetService方法。然而,自从您没有将控制器注册到 MS.DI 容器中,它将返回null。这会导致默认IHttpControllerActivator尝试使用反射创建控制器,但这需要一个默认构造函数。由于控制器没有,这会导致相当神秘的异常消息。

因此,快速的解决方案是注册您的控制器,例如:

services.AddTransient<TestController>();

然而,这只能部分解决你的问题,因为your IDependencyResolver实施被破坏。它以一种丑陋的方式被破坏,因为它一开始似乎可以工作,但会导致内存泄漏,因为你always从根容器解析,而不是从范围解析。这将导致您解析的控制器实例(和其他一次性瞬态组件)在应用程序的生命周期内保持引用。

要解决此问题,您应该更改您的IDependencyResolver实施如下:

public class DependencyResolver : IDependencyResolver
{
    private readonly IServiceProvider provider;
    private readonly IServiceScope scope;

    public DependencyResolver(ServiceProvider provider) => this.provider = provider;

    internal DependencyResolver(IServiceScope scope)
    {
        this.provider = scope.ServiceProvider;
        this.scope = scope;
    }

    public IDependencyScope BeginScope() =>
        new DependencyResolver(provider.CreateScope());

    public object GetService(Type serviceType) => provider.GetService(serviceType);
    public IEnumerable<object> GetServices(Type type) => provider.GetServices(type);
    public void Dispose() => scope?.Dispose();
}

这一实施将确保新的IServiceScope根据每个 Web 请求创建,并且服务始终根据请求解析;不是从根源上IServiceProvider.

尽管这可以解决您的问题,但其他实现可能仍然有益。

The IDependencyResolver合同有问题,因为被迫退货null当打电话给GetService不会导致注册的正确解析。这意味着当您忘记注册控制器时,您最终会遇到这些恼人的“确保控制器具有无参数公共构造函数”错误。

因此,创建自定义的要容易得多IHttpControllerActivator反而。在这种情况下你可以打电话GetRequiredService这将never return null:

public class MsDiHttpControllerActivator : IHttpControllerActivator
{
    private readonly ServiceProvider provider;

    public MsDiHttpControllerActivator(ServiceProvider provider) =>
        this.provider = provider;

    public IHttpController Create(
        HttpRequestMessage request, HttpControllerDescriptor d, Type controllerType)
    {
        IServiceScope scope = this.provider.CreateScope();
        request.RegisterForDispose(scope); // disposes scope when request ends
        return (IHttpController)scope.ServiceProvider.GetRequiredService(controllerType);
    }
}

This MsDiHttpControllerActivator可以将实现添加到 Web API 管道中,如下所示:

GlobalConfiguration.Configuration.Services
  .Replace(typeof(IHttpControllerActivator),
    new MsDiHttpControllerActivator(services.BuildServiceProvider(true)));

这消除了需要有一个IDependencyResolver执行。不过,您仍然需要注册控制器:

services.AddTransient<TestController>();

另请注意,我对此进行了更改:

services.BuildServiceProvider()

To this:

services.BuildServiceProvider(true)

这是一个非常重要的改变;它可以保护您(在某些方面)免受强制依赖项 https://blog.ploeh.dk/2014/06/02/captive-dependency/,这是使用 DI 容器时的主要问题之一。由于一些不为人知的原因,BuildServiceProvider()过载默认为false,这意味着它不会验证您的范围。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MVC5 Web API 和依赖注入 的相关文章

  • 使用 std::packaged_task/std::exception_ptr 时,线程清理程序报告数据争用

    我遇到了线程清理程序 TSan 的一些问题 抱怨某些生产代码中的数据争用 其中 std packaged task 通过将它们包装在 std function 中而移交给调度程序线程 对于这个问题 我简化了它在生产中的作用 同时触发 TSa
  • 未提供参数时如何指定 C# System.Commandline 行为?

    在我的控制台应用程序中 当未提供控制台参数时 将执行我指定列表 在本例中为参数 3 的任何处理程序 调用该处理程序时 布尔参数设置为 false 但对我来说 根本不调用它更有意义 如何防止这种情况发生并显示帮助文本 using System
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • Directory.Delete 之后 Directory.Exists 有时返回 true ?

    我有非常奇怪的行为 我有 Directory Delete tempFolder true if Directory Exists tempFolder 有时 Directory Exists 返回 true 为什么 可能是资源管理器打开了
  • 如何将 protobuf-net 与不可变值类型一起使用?

    假设我有一个像这样的不可变值类型 Serializable DataContract public struct MyValueType ISerializable private readonly int x private readon
  • 如何让 Swagger 插件在自托管服务堆栈中工作

    我已经用 github 上提供的示例重新提出了这个问题 并为任何想要自己运行代码的人提供了一个下拉框下载链接 Swagger 无法在自托管 ServiceStack 服务上工作 https stackoverflow com questio
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 在 LINQ 中按 Id 连接多表和分组

    我想按categoryId显示列表产品的名称组 这是我的代码 我想要我的视图显示结果 Desktop PC HP Red PC Dell Yellow PC Asus Red SmartPhone Lumia 720 Blue 我的组模型
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 如何使用 LINQ2SQL 连接两个不同上下文的表?

    我的应用程序中有 2 个数据上下文 不同的数据库 并且需要能够通过上下文 B 中的表的右连接来查询上下文 A 中的表 我该如何在 LINQ2SQL 中执行此操作 Why 我们正在使用 SaaS 产品来跟踪我们的时间 项目等 并希望向该产品发
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • C# 中的合并运算符?

    我想我记得看到过类似的东西 三元运算符 http msdn microsoft com en us library ty67wk28 28VS 80 29 aspx在 C 中 它只有两部分 如果变量值不为空 则返回变量值 如果为空 则返回默
  • AES 128 CBC 蒙特卡罗测试

    我正在 AES 128 CBC 上执行 MCT 如中所述http csrc nist gov groups STM cavp documents aes AESAVS pdf http csrc nist gov groups STM ca
  • “接口”类似于 boost::bind 的语义

    我希望能够将 Java 的接口语义与 C 结合起来 起初 我用过boost signal为给定事件回调显式注册的成员函数 这非常有效 但后来我发现一些函数回调池是相关的 因此将它们抽象出来并立即注册所有实例的相关回调是有意义的 但我了解到的
  • 使用管道时,如果子进程数量大于处理器数量,进程是否会被阻塞?

    当子进程数量很大时 我的程序停止运行 我不知道问题是什么 但我猜子进程在运行时以某种方式被阻止 下面是该程序的主要工作流程 void function int process num int i initial variables for
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • C++ 条件编译

    我有以下代码片段 ifdef DO LOG define log p record p else define log p endif void record char data 现在如果我打电话log hello world 在我的代码中

随机推荐