在构造函数中注入服务与 C# 中的 createScope() 比较

Sam*_*uel 10 c# .net-core

我最近开始学习c#,我对方法存在的原因感到困惑.CreateScope()

我以前从事过JAVA工作,但我无法理解它。如果我们需要一个服务作为依赖项,我们可以将其注入构造函数中,然后我们就可以使用它了。

根据我的理解,我们可以使用它创建一个新的服务范围,这在应用程序的某些引导时可能很有用。我通读了SO 的这篇文章这篇文章MS 的这篇文章,但我真的认为我还没有理解它的意义。

我看到人们创造

public constructor( IServiceScopeFactory scopeFactory,......)
{
 this.scopeFactory = scopeFactory;
}
Run Code Online (Sandbox Code Playgroud)

然后在方法中使用它

using var scope = scopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();

await mediator.Send(....);
Run Code Online (Sandbox Code Playgroud)

但我们也可以注入 IMediator,对吗?

请在这里帮助我。谢谢 !

pok*_*oke 43

依赖注入容器Microsoft.Extensions.DependencyInjection 通常用作现代 .NET 应用程序(例如 ASP.NET Core)中的默认容器,它支持三种不同的服务生命周期:瞬态、单例和作用域。服务\xe2\x80\x99s 生命周期决定特定实例的生存时间以及当多个事物依赖于该服务时如何重用它。

\n

两个更简单的生命周期是瞬态和单例:具有瞬态生命周期的服务本质上意味着每当构建依赖于它的其他服务时就会创建一个新服务。例如,如果您有两个服务 A 和 B 依赖于临时服务 D,那么将有两个 D 实例:一个用于 A,一个用于 B。但是,如果 D 是单例服务,则 A 和 B 都会会得到相同的实例。注册为单例的服务只会有一个实例,并且该实例每次都会被重用。单个服务实例将在应用程序的整个生命周期中保留在内存中,并且仅在应用程序关闭时才会被删除。

\n

现在,瞬态和单例已经在很多时候提供了帮助。但在某些情况下,您希望将这些方法混合使用。一个常见的例子是数据库连接:连接到数据库的成本有点高,因此您通常希望避免为运行的每个查询创建连接。但另一方面,您不想让连接永远保持打开状态,并且您也不希望将整个应用程序限制为单个连接,因为数据库连接通常不允许并行如果您的应用程序有并发用户,则查询 \xe2\x80\x93 一些您想要执行的操作。

\n

因此,我们的想法是为每个用户建立一个连接,当用户仍然存在时可以重复使用该连接。但每个用户都有自己独立的连接。在 Web 上下文中,例如 ASP.NET Core 应用程序, \xe2\x80\x9cuser\xe2\x80\x9d 通常表示单个请求:因此,每个传入的 HTTP 请求都会获得自己的数据库连接,该连接会尽可能地重用那个单一的请求

\n

为了实现这一点,依赖注入容器支持范围。作用域基本上是一种构造,只要作用域仍然打开,它就会保持在作用域生命周期内注册的服务实例。例如,在 ASP.NET Core 中,每当传入 HTTP 请求时就会创建一个新作用域。需要数据库连接的服务将从该请求作用域获取数据库连接。这确保每个请求只有一个数据库连接,但每个请求仍然独立于其他请求。

\n

在像 ASP.NET Core 这样的框架中,您通常不需要处理作用域的创建。框架会处理它,您只需在正常上下文中解析范围服务,一切都会正常进行。但有一个例外:单例服务。

\n

如果您有单例服务,则该服务实例独立于范围。它是在应用程序早期的某个时候创建​​的,并且无论服务范围的生命周期如何,都会共享该单个实例。\xe2\x80\x99 通常是您想要对单例执行的操作,因此一切都很好。但是,当单例服务现在需要数据库连接(这是范围依赖项)时会发生什么?由于单例服务本身不属于作用域,因此容器应如何解析作用域依赖关系?应该使用哪个范围?如果有多个并行作用域并且都具有数据库连接怎么办?如果根本没有范围怎么办?

\n

答案基本上是 \xe2\x80\x9cno\xe2\x80\x9d:通常,您不能让单例服务依赖于作用域服务,并且尝试这样做将在运行时失败。相反,如果您的单例服务需要范围依赖项(例如数据库连接),那么它应该创建自己的范围,在其中可以解析范围依赖项。为此,它可以使用IServiceScopeFactory

\n
public class MySingletonService\n{\n    private readonly IServiceScopeFactory _serviceScopeFactory;\n    public MySingletonService(IServiceScopeFactory serviceScopeFactory)\n    {\n        _serviceScopeFactory = serviceScopeFactory;\n    }\n\n    public void DoSomething()\n    {\n        // create a new scope\n        using (var scope = _serviceScopeFactory.CreateScope())\n        {\n            // resolve a database connection\n            var db = scope.ServiceProvider.GetService<IDatabaseConnection>();\n\n            // do something with it\n            db.RunQuery();\n\n\n        // the using make sure that the scope is removed afterwards,\n        // cleaning up all created instances, and e.g. closing database\n        // connections\n        }\n     }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • @silkfire 确实,这将利用服务定位器,以范围服务提供者的形式。然而,在管理范围时,这实际上是唯一的方法,因此在这些情况下它并不完全被视为反模式。如果您发现自己正在解析来自服务提供商的多个作用域服务,那么一个好主意是引入一个新的(作用域)服务来完成工作,因此您只需通过服务定位器解析单个服务(然后注入其依赖项)照常)。 (7认同)
  • 我所有的疑虑都解开了。我对你感激不尽! (2认同)