Net Core - 聊聊CreateScope
依赖注入容器Microsoft.Extensions.DependencyInjection通常在现代.NET应用程序(例如ASP.NET核心)中用作默认容器,它支持三种不同的服务生命周期:瞬时、单例和作用域。服务的生命周期决定了一个特定实例的生命周期,以及当多个事物依赖于该服务时,如何重用该实例。
两个简单的生命周期是瞬时生命周期和单体生命周期:具有瞬时生命周期的服务本质上意味着只要构建了依赖于它的其他服务,就会创建一个新服务。例如,如果您有两个依赖于临时服务D的服务A和B,那么将有两个D实例:一个用于A,一个用于B。但是,如果D是单例服务,那么A和B将获得相同的实例。注册为Singleton的服务将只有一个实例,并且每次都将重用该实例。单个服务实例将在应用程序的整个生命周期内保留在内存中,并且只有在应用程序关闭时才会被删除。
现在,瞬态和单态已经在很大程度上起到了帮助作用。但在有些情况下,你需要将它们混合在一起。一个常见的例子是数据库连接:连接到数据库的成本有点高,因此您通常希望避免为运行的每个查询创建连接。但另一方面,您不希望永远保持连接打开,也不希望将自己限制为整个应用程序的单个连接,因为数据库连接通常不允许并行查询—如果应用程序有并发用户,您希望这样做。
因此,我们的想法是为每个用户建立一个连接,在用户仍然在的情况下重新使用该连接。但每个用户都有自己的独立连接。在web环境中,例如,对于ASP.NET核心应用程序,“用户”通常意味着一个请求:因此每个传入的HTTP请求都有自己的数据库连接,该连接被尽可能多地用于该单个请求。
为了实现这一点,依赖项注入容器支持作用域。作用域基本上是一种构造,只要作用域仍处于打开状态,就可以使用作用域生存期注册服务实例。例如,在ASP.NET内核中,每当HTTP请求进入时,就会创建一个新的作用域。需要数据库连接的服务将从该请求范围获得数据库连接。这确保每个请求只有一个数据库连接,但每个请求仍然独立于其他请求。
在像ASP.NETCore这样的框架中,通常不需要处理范围的创建。框架将处理它,您可以在正常上下文中解析作用域服务,一切都将正常工作。但有一个例外:单例服务。
如果您有一个单例服务,那么该服务实例独立于作用域。它在应用程序的早期某个时候创建,并且无论服务范围的生命周期如何,都会共享单个实例。这通常是你想对单身汉做的,所以一切都很好。但是,当singleton服务现在需要一个数据库连接时会发生什么情况,这是一个作用域依赖关系?既然单例服务本身不是作用域的一部分,容器应该如何解决作用域依赖关系?它应该使用哪个范围?如果有多个并行作用域都有数据库连接怎么办?如果根本没有范围怎么办?
答案基本上是“否”:一般来说,您不能让单例服务依赖于作用域服务,并且尝试这样做将在运行时失败。相反,如果您的单例服务需要作用域依赖项,例如数据库连接,那么它应该创建自己的作用域,以便从中解析作用域依赖项。为此,它可以使用IServiceScopeFactory:
1 public class MySingletonService 2 { 3 private readonly IServiceScopeFactory _serviceScopeFactory; 4 public MySingletonService(IServiceScopeFactory serviceScopeFactory) //IServiceProvider services 5 { 6 _serviceScopeFactory = serviceScopeFactory; 7 } 8 9 public void DoSomething() 10 { 11 //IServiceProvider services 有扩展方法的 CreateScope() 12 //using IServiceScope serviceScope = services.CreateScope(); IServiceProvider services 中创建方法 13 //IServiceProvider provider = serviceScope.ServiceProvider; 14 15 16 17 // create a new scope 18 //创建一个 IServiceScope,其中包含用于解析新创建的作用域中的依赖项的 IServiceProvider。 19 using (var scope = _serviceScopeFactory.CreateScope()) //原来是在这里用的!!!! 20 { 21 // resolve a database connection 22 var db = scope.ServiceProvider.GetService<IDatabaseConnection>(); 23 24 // do something with it 25 db.RunQuery(); 26 27 28 // the using make sure that the scope is removed afterwards, 29 // cleaning up all created instances, and e.g. closing database 30 // connections 31 } 32 } 33 }
浙公网安备 33010602011771号