由于方法调用而嵌套的DbContext - Entity Framework

Tec*_*Fun 32 entity-framework

在以下情况中,由于方法调用而嵌套了两个DbContexts:

public void Method_A() {
    using (var db = new SomeDbContext()) {
        //...do some work here
        Method_B();
        //...do some more work here
    }
}

public void Method_B() {
    using (var db = new SomeDbContext()) {
        //...do some work
    }
}
Run Code Online (Sandbox Code Playgroud)

题:

  1. 这种嵌套会导致任何问题吗?(并且正确的DbContext会在正确的时间处理吗?)

  2. 如果将Method_A重构为:这种嵌套被认为是不好的做法:

    public void Method_A() {
        using (var db = new SomeDbContext()) {
            //...do some work here
        }
    
        Method_B();
    
        using (var db = new SomeDbContext()) {
            //...do some more work here
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

谢谢.

Oll*_*lly 39

您的DbContext派生类实际上在这里为您管理至少三件事:

  • 描述数据库和实体模型的元数据,
  • 底层数据库连接,和
  • 使用上下文加载的实体的客户端"缓存",用于更改跟踪,关系修正等.(请注意,虽然我将此称为"缓存"以获得更好的单词,但这通常是短暂的,只是为了支持EFs功能.如果适用,它不能替代您的应用程序中的正确缓存.)

实体框架通常缓存元数据(第1项),以便所有上下文实例(或至少所有使用相同连接字符串的实例)共享它.所以这里没有理由让你担心.

如其他注释中所述,您的代码会导致使用两个数据库连接.这对您来说可能是也可能不是问题.

您最终还有两个客户端缓存(第3项).如果您碰巧从外部上下文加载实体,那么再次从内部上下文加载实体,您将在内存中有两个副本.这肯定会令人困惑,并可能导致微妙的错误.这意味着,如果您不想使用共享上下文对象,那么您的选项2可能比选项1更好.

如果您正在使用交易,则需要进一步考虑.拥有多个数据库连接可能会导致事务被提升为分布式事务,这可能不是您想要的.既然你没有提到数据库事务,我不会在这里进一步讨论.

那么,这会让你离开?

如果您使用此模式只是为了避免DbContext在代码中传递对象,那么您可能最好重构MethodB以接收上下文作为参数.应该反复出现长期上下文对象的问题.根据经验,为单个数据库操作或一系列相关数据库操作创建新上下文.(例如,请参阅此博客文章此问题.)

(作为替代方案,您可以向DbContext接收现有连接的派生类添加构造函数.然后,您可以在多个上下文之间共享相同的连接.)

一个有用的模式是编写自己的类,创建上下文对象并将其存储为私有字段或属性.然后你创建类实现IDisposable,它的Dispose()方法处理上下文对象.您的呼叫代码会通知您班级的实例,而不必担心上下文或连接.

什么时候你可能需要同时激活多个上下文?

当您需要编写多线程代码时,这非常有用.数据库连接不是线程安全的,因此您必须一次只能从一个线程访问连接(因此也就是EF上下文).如果限制太多,则需要多个连接(和上下文),每个线程一个.你可能会发现很有趣.


小智 0

如果您认为与数据库的连接数量以及必须打开新连接的次数的影响不是一个重要问题,并且您对支持应用程序以最佳性能运行没有任何限制,那么一切都可以。你的代码运行良好。因为仅创建数据库上下文对性能影响很小,所以元数据将在首次加载后缓存,并且仅在代码需要执行查询时才连接到数据库。通过很少的性能考虑和代码设计,我建议您创建上下文工厂,以便为应用程序的每个实例提供每个数据库上下文的一个实例。

您可以查看此链接以了解更多性能注意事项 http://msdn.microsoft.com/en-us/data/hh949853