【问题标题】:ASP.NET Core - Repository dependency injection fails on Singleton injectionASP.NET Core - 存储库依赖注入在单例注入时失败
【发布时间】:2019-12-21 01:10:06
【问题描述】:

我正在使用SoapCore 为我的 ASP.NET Core MVC 应用程序创建 Web 服务。

我正在使用 Entity Framework Core 和一个简单的存储库模式来获取我的数据库数据。

我通过 .AddSingleton() 在我的 Startup.cs 中注入我的存储库类:

services.AddSingleton<IImportRepository, ImportRepository>();
services.AddSingleton<IWebService, WebService>();

由于 EF DbContext 是作用域的,因此在调用我的 Web 服务时出现错误:

无法使用范围服务“App.Data.ApplicationDbContext” 单例“App._Repository.IImportRepository”。

当我改用.AddScoped() 时,它工作正常。

我读过通过 controllers/classes 构造函数 注入作用域依赖项是不好的做法,因为它会“退回”为单例或行为类似单例。

我想知道当通过 ctor 在我的控制器中使用作用域注入时,是否有另一种方法可以使其与单例一起使用,或者从长远来看,这是否有一些主要缺点(大约 100-200 个用户将使用该站点)?

【问题讨论】:

  • 通过控制器/类构造函数注入作用域依赖项是不好的做法 这不是真的。控制器也是限定范围的,并且是为您的 WebApp/Api 的每个请求创建的。所以注入你需要的东西是一个很好的做法,它会在请求后自动清理。
  • 100-200 个用户就是个笑话。除非每个用户每秒执行 100-1000 个请求,否则您甚至不应该打扰。否则,做一个基准,决定。构造函数应该很快,如果你在构造函数中做了一些需要很长时间的时髦的事情,你就错了
  • “我读过通过控制器/类构造函数注入作用域依赖项是不好的做法”。请向我们提供链接和报价,以便我们检查来源。将作用域依赖注入到控制器或构造器中没有任何问题。这是很常见的事情。
  • 请理解Captive Dependencies的概念。

标签: c# asp.net-core dependency-injection asp.net-core-mvc entity-framework-core


【解决方案1】:

简单地说,您的首选生命周期应该是“范围”的。如果您有好的理由这样做,您应该只使用单例或瞬态生命周期。对于单例来说,这就像管理锁或持有需要在应用程序的生命周期内持久保存的数据之类的东西,这两者都不适用于存储库的概念。存储库应该完全是一次性的。关键是要持久化到数据库或其他存储,因此它们本身不应包含任何需要持久化的数据。

无论长短,你最好的选择是简单地将你的 repo(s) 限定为范围,这样你就可以直接注入上下文。就构造函数注入而言,我不确定您从哪里知道这是一种不好的做法。事实上,在大多数情况下,依赖注入就是这样工作的,所以你不能真正拥有一个没有另一个。

如果您绝对需要拥有一个单例,那么您唯一的选择就是服务定位器反模式。为此,您将注入IServiceProvider

public class MyRepo
{
    private readonly IServiceProvider _serviceProvider;

    public MyRepo(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    ...
}

然后,每次您需要上下文时(这很重要),您需要这样做:

using (var scope = _serviceProvider.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<MyContext>();
    // do something with context
}

【讨论】:

    【解决方案2】:

    作用域对象不能注入到单例对象中。就这么简单,单例只在应用程序启动时创建一次,并被所有后续请求使用。范围对象在每个请求期间创建并在请求结束时处理。因此,先前创建的单个对象无法了解在每个请求期间创建的作用域对象。所以在单例中使用作用域对象是不可能的。但反过来也是可能的。

    通过控制器/类构造函数注入作用域依赖

    我不认为这是一个坏习惯。如果没有,您打算如何进行单元测试?

    另外,将数据库上下文设为单例也是不正确的。您将面临并行和后续请求的兑现问题/数据异常。在我看来,必须对 DB 上下文进行限定,并且所有使用 db-context 的对象都必须一直向上限定。

    所以在你的情况下,将所有对象 ImportRepository、WebService 和 DB Context 都设置为范围。

    干杯,

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-16
      • 2018-03-23
      相关资源
      最近更新 更多