实体框架核心服务默认生命周期

Rem*_*Rem 38 entity-framework-core asp.net-core

在ASP.NET Core应用程序中,我可以像这样通过DI注册DbContext

services.AddDbContext<Models.ShellDbContext>(options => options.UseNpgsql(connection));
Run Code Online (Sandbox Code Playgroud)

而知道它的生命周期是什么呢?

从这里https://github.com/aspnet/EntityFramework/blob/f33b76c0a070d08a191d67c09650f52c26e34052/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L140看起来它被配置为作用域,这意味着的DbContext实例在每次请求创建.

所以问题的第一部分是:它是真的,如果是,那么它的代价是多少?

第二部分是:如果我创建一个消耗DbContext的服务,并且打算由控制器使用,并且将有一个API来管理DB中的某些实体,它是否应该注册为Scoped?

Tse*_*eng 54

是,DbContext作用域的默认生命周期.这是这样的.

实例化DbContext非常便宜,它确保您不会使用许多资源.如果你有一个DbContext单身生命周期,那么你读过的所有记录都将被跟踪DbContext,除非你专门禁用跟踪.这将需要更多的内存使用,并将继续增长.

而更多的DbContext轨道,性能就越低.这就是为什么你经常看到DbContext只在一个using(var context = new AppDbContext())区块内使用.

但是,在Web应用程序中,使用using块是不好的,因为生命周期由框架管理,如果您将其初始处理,则之后的调用将失败并出现异常.

如果您在另一侧使用瞬态生命周期,则将失去"事务"功能.使用作用域时,DbContext具有与请求一样长的事务范围.

如果您需要更细粒度的控制,则必须使用工作单元模式(DbContext已经使用的模式).

对于你的第二个问题:

如果您创建服务,它的生命周期必须等于范围或更短的范围(读取:Scoped或瞬态).

如果您明确需要更长的服务生命周期,则应将DbContext工厂服务或工厂方法注入服务中.

你可以用类似的东西来完成这个

services.AddTransient<Func<AppDbContext>>( (provider) => new Func<MyDbContext>( () => new AppDbContext()));
services.AddSingleton<IMySingletonService, MySingletonService>();
Run Code Online (Sandbox Code Playgroud)

您的服务可能如下所示:

public class MySingletonService : IMySingletonService, IDisposable
{
    private readonly AppDbContext context;

    public MySingletonService(Func<AppDbContext> contextFactory)
    {
        if(contextFactory == null)
            throw new ArgumentNullException(nameof(contextFactory));

        // it creates an transient factory, make sure to dispose it in `Dispose()` method.
        // Since it's member of the MySingletonService, it's lifetime
        // is effectively bound to it. 
        context = contextFactory();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在网络应用程序中它真的是管理生命周期的控制器吗?或者是在请求结束时根据请求处理范围内的事物的DI?我想更好地理解 (2认同)

Sim*_*ver 13

注意:在EF Core 2中,现在有一种新方法AddDbContextPool可以创建可以重用的上下文池.范围仍然相同,但实例将被"重置"并返回池中.我原本以为"重置"的开销与创建新的开销相同,但我想情况并非如此.

如果使用此方法,则在控制器请求DbContext实例时,我们将首先检查池中是否有可用的实例.请求处理完成后,将重置实例上的任何状态,并将实例本身返回到池中.+

这在概念上类似于连接池在ADO.NET提供程序中的操作方式,并且具有节省DbContext实例初始化的一些成本的优点.

https://docs.microsoft.com/en-us/ef/core/what-is-new/

  • 猜测可能是“OnModelCreating”函数在大型数据库上花费时间? (2认同)