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

时间:2016-05-29 08:17:55

标签: asp.net-core entity-framework-core

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

services.AddDbContext<Models.ShellDbContext>(options => options.UseNpgsql(connection));

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

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

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

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

3 个答案:

答案 0 :(得分:46)

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

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

DbContext曲目越多,性能就越低。这就是为什么您经常看到DbContext仅在using(var context = new AppDbContext())块中使用的原因。

然而,在Web应用程序中,使用using块是不好的,因为生命周期由framework管理,如果您将其早期处理,则之后的调用将因异常而失败。

如果您在另一方使用瞬态寿命,您将失去“交易”功能。使用作用域时,DbContext的事务范围与请求一样长。

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

关于你的第二个问题:

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

如果您明确需要更长的服务续航时间,则应在服务中注入DbContext工厂服务或工厂方法。

您可以使用类似

的内容来完成此操作
services.AddTransient<Func<AppDbContext>>( (provider) => new Func<MyDbContext>( () => new AppDbContext()));
services.AddSingleton<IMySingletonService, MySingletonService>();

您的服务可能如下所示:

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

答案 1 :(得分:11)

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

  

如果使用此方法,则在请求DbContext实例时   通过控制器,我们将首先检查是否有可用的实例   在游泳池。一旦请求处理完成,就会有任何状态   实例重置,实例本身返回池。+

     

这在概念上类似于连接池的运作方式   ADO.NET提供商具有节省一些成本的优势   初始化DbContext实例。

https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#high-performance

答案 2 :(得分:0)

这是我所学到的轶事。这是因为懒惰的KISS热爱。我避免了其他麻烦,并解决了我的过早EF Core DbContext问题,不再考虑池,范围等问题。我只是把不考虑异步返回值的POC放在一起,从控制器链接到存储库,等等。 ,所有这些本质上都返回“ void”,即单数形式的“ Task”。这导致我的DbContext成员在较低例程中进行迭代,从而莫名其妙地处理了基础DbContext。我要做的就是让每个异步方法返回一个def get_batch(image,label,image_W,image_H,batch_size,capacity): image = tf.cast(image,tf.string) label = tf.cast(label,tf.int32) #tf.cast()用来做类型转换 input_queue = tf.train.slice_input_producer([image,label]) #加入队列 label = input_queue[1] image_contents = tf.read_file(input_queue[0]) image = tf.image.decode_jpeg(image_contents,channels=3,try_recover_truncated = True,acceptable_fraction=0.5) #jpeg或者jpg格式都用decode_jpeg函数,其他格式可以去查看官方文档 image = tf.image.resize_image_with_crop_or_pad(image,image_W,image_H) #resize image = tf.image.per_image_standardization(image) #对resize后的图片进行标准化处理 image_batch,label_batch = tf.train.batch([image,label],batch_size = batch_size,num_threads=16,capacity = capacity) label_batch = tf.reshape(label_batch,[batch_size]) image_batch = tf.cast(image_batch,tf.float32) return image_batch,label_batch 值,并且一切正常。 EF Core不喜欢异步void返回值。