4

Documentation我应该如何管理MVC Core中的DbContext生命周期?

实体框架上下文应该被添加到使用Scoped终身服务容器 。如果您使用如上所示的帮助程序方法,则会自动执行此操作。使用实体框架 的存储库应该使用相同的生命周期。

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

documentation读取如下:

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

  • Scoped对象是请求中的相同,但不同的跨越不同的要求

再回到ServiceAServiceB,它的声音对我来说,Transient更适合。

我研究过,上下文应该只保存一次HttpRequest,但我真的不明白这是如何工作的。

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

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

我错过框架的一些重要部分吗?

+0

切勿使用临时'DbContext'注册。瞬态生命周期将创建一个服务的新实例**每个**它是另一个服务请求的时间。这将导致每个请求有多个'DbContext's。在同一请求中重复使用相同的'DbContext'实例没有问题:每个请求都绑定到单个线程,所以根本没有线程安全问题。 –

+0

但是如果服务因为某种原因(例如Task.Start)并行处理会发生什么? @FedericoDipuma –

+0

你的意思是*并行*?你是否明确地产生了一个新的线程,你可以使用相同的'DbContext'? –

回答

5

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

如果你做需要线程,即后台工作人员,那么它们可能会有不同于请求的生命周期。因此,那些线程应该使用而不是使用从请求范围检索的依赖关系。当请求结束并且其依赖关系范围被关闭时,一次性依赖关系将被正确处置。对于其他线程,这意味着它们的依赖可能最终被丢弃,虽然他们仍然需要它们:坏主意。

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

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

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

BackgroundThreadService及其所有的依赖则可以按照接收依赖的共同依赖注入方式。

+0

非常好:)谢谢你的详细解释。 –

2

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

Lifetimes它只是一种方式来存储您的数据(这里很简单)。只需看看流行的DI框架中的一些终身管理者,他们所有的工作都非常匹配 - 这只是像实现一次性模式的对象那样的字典。使用瞬态我相信你的get对象方法将总是返回null,所以DI会在每次请求时创建新的实例。SingleInstance将存储对象类似静态并发字典的东西,因此容器将只创建一次实例,然后接收现有的实例。

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

简而言之,不要担心只是使用作用域是安全的,您可以根据请求调用它。

我试着在我的解释很简单,你可以随时看看源代码找到,因为比赛的细节,因为你需要在这里https://github.com/aspnet/DependencyInjection

相关问题