我最近开始在我的 .NET 4.0 应用程序中使用 Entity Framework 4.0,并对与池相关的一些事情感到好奇。
据我所知,连接池由 ADO.NET 数据提供程序管理,在我的例子中是 MS SQL 服务器。这在您实例化一个新的实体上下文 (ObjectContext) 时是否适用,即无参数的 new MyDatabaseModelEntities()? a) 为应用程序创建全局实体上下文(即一个静态实例)或 b) 使用 using 块为每个给定操作/方法创建和公开实体上下文的优点和缺点是什么。对于某些场景,我应该了解的任何其他建议、最佳实践或常用方法?
连接池的处理方式与任何其他 ADO.NET 应用程序一样。实体连接仍然使用传统的数据库连接和传统的连接字符串。我相信如果您不想使用它,可以关闭连接字符串中的连接池。 (阅读有关 SQL Server 连接池 (ADO.NET) 的更多信息)永远不要使用全局上下文。 ObjectContext 在内部实现了几种模式,包括身份映射和工作单元。使用全局上下文的影响因应用程序类型而异。对于 Web 应用程序,每个请求使用单个上下文。对于 Web 服务,每次调用使用单个上下文。在 WinForms 或 WPF 应用程序中,每个表单或每个演示者使用单个上下文。可能有一些特殊要求不允许使用这种方法,但在大多数情况下这已经足够了。
如果您想知道 WPF/WinForm 应用程序的单个对象上下文有什么影响,请查看此 article。这是关于 NHibernate Session 但想法是一样的。
编辑:
当您使用 EF 时,默认情况下每个上下文仅加载每个实体一次。第一个查询创建实体实例并将其存储在内部。任何需要具有相同键的实体的后续查询都会返回此存储的实例。如果数据存储中的值发生更改,您仍会收到带有初始查询值的实体。这称为身份映射模式。您可以强制对象上下文重新加载实体,但它会重新加载单个共享实例。
在您对上下文调用 SaveChanges
之前,不会保留对实体所做的任何更改。您可以对多个实体进行更改并一次存储它们。这称为工作单元模式。您不能有选择地说出要保存哪个修改后的附加实体。
结合这两种模式,你会看到一些有趣的效果。整个应用程序只有一个实体实例。即使更改尚未持久化(提交),对实体的任何更改也会影响整个应用程序。在大多数情况下,这不是您想要的。假设您在 WPF 应用程序中有一个编辑表单。您正在使用实体并决定取消复杂的编辑(更改值、添加相关实体、删除其他相关实体等)。但是实体已经在共享上下文中进行了修改。你会怎么做?提示:我不知道 ObjectContext
上有任何 CancelChanges 或 UndoChanges。
我认为我们不必讨论服务器场景。简单地在多个 HTTP 请求或 Web 服务调用之间共享单个实体会使您的应用程序毫无用处。任何请求都可以触发 SaveChanges
并保存来自另一个请求的部分数据,因为您在所有请求之间共享单个工作单元。这也会带来另一个问题——上下文以及对上下文中实体的任何操作或上下文使用的数据库连接都不是线程安全的。
即使对于只读应用程序,全局上下文也不是一个好的选择,因为您可能每次查询应用程序时都需要新数据。
根据丹尼尔西蒙斯的说法:
在 Using 语句中为每个服务方法创建一个新的 ObjectContext 实例,以便在方法返回之前将其处理掉。此步骤对于您的服务的可扩展性至关重要。它确保数据库连接不会在服务调用之间保持打开状态,并且特定操作使用的临时状态在该操作结束时被垃圾收集。 Entity Framework 会自动在应用程序域中缓存元数据和它需要的其他信息,并且 ADO.NET 将数据库连接池化,因此每次重新创建上下文是一个快速操作。
这是来自他的综合文章:
http://msdn.microsoft.com/en-us/magazine/ee335715.aspx
我相信这个建议可以扩展到 HTTP 请求,因此对 ASP.NET 也是有效的。有状态的胖客户端应用程序(例如 WPF 应用程序)可能是“共享”上下文的唯一情况。
IDisposable
,因此应该在最短的合理时间内打开,这是我的看法。
根据 EF6(也有 4,5)文档:https://msdn.microsoft.com/en-us/data/hh949853#9
9.3 每个请求的上下文
实体框架的上下文旨在用作短期实例,以提供最佳性能体验。上下文预计将是短暂的并被丢弃,因此已实现非常轻量级并尽可能重用元数据。在 Web 场景中,记住这一点很重要,并且上下文的持续时间不会超过单个请求的持续时间。同样,在非 Web 场景中,应根据您对实体框架中不同级别缓存的理解丢弃上下文。一般来说,应该避免在应用程序的整个生命周期中使用上下文实例,以及每个线程的上下文和静态上下文。
下面的代码帮助我的对象用新的数据库值刷新。 Entry(object).Reload() 命令强制对象调用数据库值
GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
TransactionScope
不属于工作单元,它属于您的业务逻辑,因为逻辑本身定义了事务。工作单元仅定义应该一起持久化的内容,而事务范围允许您在同一事务中多次使用工作单元持久性。