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

来自文档

使用Scoped生存期将entity framework上下文添加到服务容器中。 如果您使用如上所示的辅助方法,则会自动完成此操作。 将使用entity framework的存储库应使用相同的生命周期。

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

文档内容如下:

  • Transient物体总是不同的; 为每个控制器和每个服务提供一个新实例。

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

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

我研究过,每个HttpRequest只能保存一次Context,但我真的不明白这是如何工作的。

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

 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(); } 

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

同时,另一个服务可以更新相同的上下文。 根据我的阅读, DbContext不是线程安全的。

在这种情况下我应该使用Transient吗? 为什么文档建议,我应该使用Scoped

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

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

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

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

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

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

然后, BackgroundThreadService及其所有依赖项可以遵循接收依赖项的公共依赖项注入方式。

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

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

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

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

我试着在我的解释中非常简单,你可以随时查看源代码,找到你需要的匹配细节https://github.com/aspnet/DependencyInjection