【问题标题】:Entity Framework and Connection Pooling实体框架和连接池
【发布时间】:2011-04-08 20:32:34
【问题描述】:

我最近开始在我的 .NET 4.0 应用程序中使用 Entity Framework 4.0,并对与池相关的一些事情感到好奇。

  1. 据我所知,连接池由 ADO.NET 数据提供程序管理,在我的情况下是 MS SQL 服务器。这在您实例化新实体上下文 (ObjectContext) 时是否适用,即无参数 new MyDatabaseModelEntities()

  2. a) 为应用程序创建全局实体上下文(即一个静态实例)或 b) 使用using 为每个给定操作/方法创建和公开实体上下文的优点和缺点是什么阻止。

  3. 对于某些场景我应该了解的任何其他建议、最佳做法或常用方法?

【问题讨论】:

    标签: .net database entity-framework ado.net connection-pooling


    【解决方案1】:
    1. 连接池的处理方式与任何其他 ADO.NET 应用程序一样。实体连接仍然使用传统的数据库连接和传统的连接字符串。我相信如果您不想使用它,可以关闭连接字符串中的连接池。 (阅读更多关于SQL Server Connection Pooling (ADO.NET)的信息)
    2. 永远不要使用全局上下文。 ObjectContext 在内部实现了几种模式,包括身份映射和工作单元。使用全局上下文的影响因应用程序类型而异。
    3. 对于 Web 应用程序,每个请求使用单个上下文。对于 Web 服务,每次调用使用单个上下文。在 WinForms 或 WPF 应用程序中,每个表单或每个演示者使用单个上下文。可能有一些特殊要求不允许使用这种方法,但在大多数情况下这已经足够了。

    如果您想知道单个对象上下文对 WPF/WinForm 应用程序有何影响,请查看此article。这是关于 NHibernate Session 但想法是一样的。

    编辑:

    当您使用 EF 时,默认情况下每个上下文仅加载每个实体一次。第一个查询创建实体实例并将其存储在内部。任何需要具有相同键的实体的后续查询都会返回此存储的实例。如果数据存储中的值发生更改,您仍会收到带有初始查询值的实体。这称为身份映射模式。您可以强制对象上下文重新加载实体,但它会重新加载单个共享实例。

    在您在上下文中调用 SaveChanges 之前,不会保留对实体所做的任何更改。您可以对多个实体进行更改并一次存储它们。这称为工作单元模式。您不能有选择地说出要保存哪个修改后的附加实体。

    结合这两种模式,你会看到一些有趣的效果。整个应用程序只有一个实体实例。即使更改尚未持久化(提交),对实体的任何更改也会影响整个应用程序。在大多数情况下,这不是您想要的。假设您在 WPF 应用程序中有一个编辑表单。您正在使用实体并决定取消复杂的编辑(更改值、添加相关实体、删除其他相关实体等)。但是实体已经在共享上下文中进行了修改。你会怎么做?提示:我不知道 ObjectContext 上的任何 CancelChanges 或 UndoChanges。

    我认为我们不必讨论服务器场景。简单地在多个 HTTP 请求或 Web 服务调用之间共享单个实体会使您的应用程序毫无用处。任何请求都可以触发SaveChanges 并保存来自另一个请求的部分数据,因为您在所有请求之间共享单个工作单元。这还会带来另一个问题 - 上下文以及对上下文中实体的任何操作或上下文使用的数据库连接都不是线程安全的。

    即使对于只读应用程序,全局上下文也不是一个好的选择,因为您可能在每次查询应用程序时都需要新数据。

    【讨论】:

    • 感谢您的回复。也许您可以详细说明为什么使用单个全局上下文是不好的?当然,这会使并行访问更加困难,但还有什么......?
    • 好的,现在清楚多了,谢谢。只是为了确认,尽管全局上下文从来都不是真正合适的,但“编辑对话框”的单个上下文或类似的可能是正确的方式?在其他情况下,例如 Web 服务和 ASP.NET,方法中的上下文只会更有意义。关于正确吗?
    • 我听取了您的建议并删除了单曲。现在我得到另一个错误:stackoverflow.com/questions/14795899/…
    • 我理解实现工作单元模式和封装 DbContext 应该将业务逻辑和数据库操作分开。我无法理解如何实现工作单元模式并将 TransactionScope 仅用于某些操作。
    • @RudolfDvoracek:很容易。 TransactionScope 不属于工作单元,它属于您的业务逻辑,因为逻辑本身定义了事务。工作单元仅定义应该一起持久化的内容,而事务范围允许您在同一事务中多次使用工作单元持久性。
    【解决方案2】:

    根据丹尼尔·西蒙斯的说法:

    在中创建一个新的 ObjectContext 实例 每个服务的 Using 语句 方法,以便将其处理掉 在方法返回之前。 此步骤对于您的服务的可扩展性至关重要。它确保数据库连接不会在服务调用之间保持打开状态,并且特定操作使用的临时状态在该操作结束时被垃圾收集。 Entity Framework 自动缓存元数据和它需要的其他信息在应用程序域中,ADO.NET 池化数据库连接,因此每次重新创建上下文是一个快速操作。

    这是来自他的综合文章:

    http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

    我相信这个建议也适用于 HTTP 请求,因此对 ASP.NET 也是有效的。有状态的胖客户端应用程序(例如 WPF 应用程序)可能是“共享”上下文的唯一情况。

    【讨论】:

    • 谢谢,这是一个非常有用的报价。但是,我仍然想知道共享(全局)上下文是否适用于客户端 WPF 应用程序等。即使在这种情况下也有任何优势吗?
    • WPF 应用程序中的全局上下文没有优势,但也可能没有明显的损害。如果您确实实现了全局上下文,则可能需要在高请求率的情况下手动管理数据库连接(显式关闭连接)。
    • 对;所以基本上我永远不会通过使用多个临时上下文而真正出错(假设我知道正在发生连接池)? ...如果您使用单个全局上下文,理论上连接不会在随机时间点下降吗?
    • @Nolodrin:我不认为连接会“随机”断开......风险是连接可能会保持打开太久并使连接池饱和。
    • ObjectContext/ DbContext 实现IDisposable,因此应该在最短的合理时间内打开,这是我的看法。
    【解决方案3】:

    根据 EF6(也有 4,5)文档: https://msdn.microsoft.com/en-us/data/hh949853#9

    9.3 每个请求的上下文

    实体框架的上下文旨在用作短期实例,以提供最佳性能体验。上下文预计将是短暂的并被丢弃,因此已实现非常轻量级并尽可能重用元数据。在 Web 场景中,记住这一点很重要,并且上下文的持续时间不会超过单个请求的持续时间。同样,在非 Web 场景中,应根据您对实体框架中不同级别缓存的理解丢弃上下文。 一般来说,应避免在应用程序的整个生命周期中使用上下文实例,以及每个线程的上下文和静态上下文。

    【讨论】:

    • 我知道这个回复已经有一段时间了,但我不得不说这让我很头疼。将 EF 与 Oracle 一起使用时不断收到“池化连接”错误,并且无法弄清楚原因。我已将 dbContext 设置为类变量,并在创建时对其进行实例化。将其更改为根据需要创建上下文解决了我的世界的所有弊端。谢谢!
    • 你能解释一下为什么上下文应该只作用域和单例/瞬态实例吗?那么会出现什么样的错误呢?
    【解决方案4】:

    下面的代码帮助我的对象用新的数据库值刷新。 Entry(object).Reload() 命令强制对象调用数据库值

    GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
    DatabaseObjectContext.Entry(member).Reload();
    

    【讨论】:

    • 以及这个集合(VB代码):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
    猜你喜欢
    • 2011-10-13
    • 2020-11-18
    • 2011-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    相关资源
    最近更新 更多