实体框架和连接池


268

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

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

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

  3. 我应该了解某些特定情况下的其他建议,最佳实践或通用方法吗?

Answers:


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

如果您想知道对WPF / WinForm应用程序单个对象上下文有什么影响,请查阅本文。它与NHibernate Session有关,但是想法是相同的。

编辑:

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

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

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

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

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


感谢您的回复。也许您可以详细说明为什么使用单个全局上下文不好?当然,这使并行访问更加困难,但是还有什么呢?
Noldorin 2010年

好的,现在更加清楚了,谢谢。只是要确认,尽管全局上下文从来都不是真正合适的,但“编辑对话框”之类的单个上下文可能是正确的方法吗?在其他情况下(例如Web服务和ASP.NET),方法中的上下文才更有意义。关于正确?
Noldorin's

我听了你的劝告,撤下了辛格尔顿。现在我得到另一个错误:stackoverflow.com/questions/14795899/...
埃拉德奔达

我知道实现工作单元模式和封装DbContext应该将业务逻辑和数据库操作分开。我无法理解如何实现工作单元模式以及仅对某些操作使用TransactionScope。
Rudolf Dvoracek

4
@RudolfDvoracek:轻松。TransactionScope不属于工作单元,而是属于您的业务逻辑,因为逻辑本身定义了事务。工作单元仅定义应一起保留的内容,而事务范围允许您在同一事务中多次使用工作单元持久性。
Ladislav Mrnka

70

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

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

这来自他的综合文章:

http://msdn.microsoft.com/zh-CN/magazine/ee335715.aspx

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


谢谢,那是一个非常有用的报价。但是,我仍然想知道,即使对于客户端WPF应用程序或此类应用程序,共享(全局)上下文是否合适。是否有任何在这种情况下,即使优势?
Noldorin 2010年

WPF应用程序中的全局上下文不会有任何优势,但也可能不会有重大危害。如果实现了全局上下文,则在请求率很高的情况下,可能必须对数据库连接进行一些手动管理(显式关闭连接)。
Dave Swersky'9

1
对; 所以从本质上讲,我永远不会使用多个临时上下文真正出错(假设我知道连接池正在发生)?...如果使用的是单个全局上下文,理论上的联系会不会在随机的时间点掉落?
Noldorin's

1
@Nolodrin:我不认为连接会“随机”掉线……风险是连接可能保持打开时间过长并会使连接池饱和。
Dave Swersky 2010年

1
IDisposable我认为ObjectContext / DbContext工具,因此应该在最短的合理时间内打开。
nicodemus13 2012年

12

根据EF6(也包括4,5)文档:https ://msdn.microsoft.com/zh-cn/data/hh949853#9

9.3每个请求的上下文

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


2
我知道这个答复已经有一段时间了,但是我不得不说这为我节省了很多麻烦。将EF与Oracle一起使用时,会出现“池连接”错误,并且无法弄清楚原因。我已经将dbContext设置为类变量,并在创建时实例化了它。根据需要将其更改为创建上下文可以修复我这个世界的所有弊端。谢谢!
Fletchius

1

下面的代码用新的数据库值帮助刷新了我的对象。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)
伊万·费雷尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.