我应该如何在MVC Core中管理DbContext Lifetime?

时间:2017-09-25 19:25:35

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

来自Documentation

  

实体框架上下文添加到服务容器中   使用Scoped生命周期。如果您自动处理   使用如上所示的辅助方法。将使用的存储库   实体框架应使用相同的生命周期。

我一直认为,我应该为我必须处理的每一个工作单元创建一个新的Context。这让我想一想,如果我有一个ServiceAServiceB,它们会对DbContext应用不同的操作,他们应该获得DbContext的不同实例。

documentation内容如下:

  
      
  • Transient个对象总是不同的;每个控制器和每个服务都提供一个新实例。

  •   
  • Scoped个对象在请求中是相同的,但在不同请求中是不同的

  •   

回到ServiceAServiceB,对我来说,Transient更合适。

我研究过,上下文每{... 1}只能保存一次,但我真的不明白这是如何工作的。

特别是如果我们看一个服务:

HttpRequest

这里我们需要保存上下文,以获取与我们刚刚创建的另一个实体相关的实体的ID。

同时,另一项服务可以更新相同的上下文。根据我的阅读,using (var transaction = dbContext.Database.BeginTransaction()) { //Create some entity var someEntity = new SomeEntity(); dbContext.SomeEntity.Add(someEntity); //Save in order to get the the id of the entity dbContext.SaveChanges(); //Create related entity var relatedEntity = new RelatedEntity { SomeEntityId = someEntity.Id }; dbContext.RelatedEntity.Add(relatedEntity) dbContext.SaveChanges(); transaction.Commit(); } 不是线程安全的。

在这种情况下我应该使用DbContext吗?为什么文档提示,我应该使用Transient

我是否会错过框架的一些重要部分?

2 个答案:

答案 0 :(得分:22)

正如其他人已经解释的那样,应该对数据库上下文使用作用域依赖项,以确保它可以正确地重用。对于并发性,请记住您也可以异步查询数据库,因此可能不需要实际的线程。

如果你需要线程,即后台工作者,那么它们的生命周期可能与请求不同。因此,那些线程应使用从请求范围检索的依赖项。当请求结束并且其依赖范围被关闭时,将适当地处理一次性依赖性。对于其他线程,这意味着他们的依赖关系最终可能会被处理掉,尽管他们仍然需要它们:不好的想法。

相反,您应该为您创建的每个线程显式打开一个新的依赖项范围。您可以通过注入IServiceScopeFactory并使用CreateScope创建范围来实现此目的。然后,生成的对象将包含一个服务提供程序,您可以从中检索依赖项。由于这是一个单独的范围,因此将在此范围的生命周期内重新创建范围内的依赖关系,如数据库上下文。

为了避免进入服务定位器模式,您应该考虑让线程执行一个集中服务,将所有必需的依赖项集合在一起。然后线程可以这样做:

using (var scope = _scopeFactory.CreateScope())
{
    var service = scope.ServiceProvider.GetService<BackgroundThreadService>();
    service.Run();
}

BackgroundThreadService及其所有依赖关系可以遵循接收依赖关系的公共依赖注入方式。

答案 1 :(得分:2)

我相信在大多数情况下使用作用域生存期时,您不会遇到并发问题。即使在发布的示例中,也没有并发问题,因为随后将调用当前请求中的服务。我甚至无法想象在一个HTTP请求(范围)的上下文中并行运行2个或更多服务(可能但不常见)的情况。

生命周期只是存储数据的一种方式(这里很简单)。只看一下流行的DI框架中的一些终身经理,所有这些工作都非常匹配 - 这只是实现一次性模式的对象的字典。使用Transient我相信你的get对象方法将始终返回null,因此DI将在每次请求时创建新实例。 SingleInstance会将对象存储在静态并发字典之类的内容中,因此容器只会创建一次实例,然后接收现有的实例。

Scoped通常是指范围对象用于存储创建的对象。在asp网络管道中,它通常意味着与每个请求相同(因为范围可以通过管道传递)

要简短 - 不要担心只使用范围它是安全的,你可以根据要求调用它。

我在解释中试图变得非常简单,您可以随时查看源代码,以便在此处找到匹配的详细信息https://github.com/aspnet/DependencyInjection