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

Chr*_*rdt 19 c# entity-framework-core asp.net-core-mvc .net-core asp.net-core

来自文档

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

我一直认为,我应该Context为我必须处理的每一个工作单元创建一个新的.这让我想到,如果我有一个ServiceAServiceB,这些都对应用不同的行动DbContext,他们应该得到的不同实例DbContext.

文档读取如下:

  • 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();
}
Run Code Online (Sandbox Code Playgroud)

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

同时,另一个服务可以更新相同的上下文.从我读过的,DbContext不是线程安全的.

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

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

pok*_*oke 26

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

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

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

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

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

然后BackgroundThreadService,它的所有依赖关系可以遵循接收依赖关系的公共依赖注入方式.