实体框架中的POCO支持 - 第三部分 - POCO的变动跟踪

  在上一个POCO贴子里,我提到了跟踪POCO变动的两种可行性:基于快照的变动跟踪(Snapshot based Change Tracking) 和 使用代理的基于通知的变动跟踪(Notification based Change Tracking with Proxies)。 在这个贴子里,我将对这两个选项做进一步的讨论,讨论它们的优缺点,以及使用它们的含意(implications)。我们还将在《实体框架设计博客》上贴出针对至今为止受到的关于Entity Framework 4.0中POCO支持的一些反馈的想法。

基于快照的变动跟踪(不用代理的纯POCO类)

  就象我在这个系列的第二部分中提到的,基于快照(Snapshot)的变动跟踪,就是纯粹的POCO实体的做法,不使用代理来处理变动跟踪。这是个简明的变动跟踪方案,依赖于实体框架所维护的之前和之后值的完整快照,在SaveChanges中对这些值进行比较,决定到底哪些值与初始值有所不同。在这个模型中,除非你用了懒式装载,你的实体的运行时类型跟你定义的POCO实体类型是一模一样的。

  这个做法没什么问题,如果你在乎你的实体类型的运行时纯粹性(不使用代理),完全可以依赖这个方案,走纯粹的POCO实体之路(不依赖于代理类型实现额外的功能)。

  基于快照的变动跟踪唯一潜在的问题是,有几件事情你需要注意,因为在你的对象变动时没有向实体框架做直接变动通知,实体框架的对象状态管理器将与你的对象图不再同步。

  让我们来看一个展示这个问题的例子:

 1 Customer customer = (from c in context.Customers
 2                      where c.CustomerID == "ALFKI"
 3                      select c).Single();
 4
 5 ObjectStateEntry ose = context.ObjectStateManager.GetObjectStateEntry(customer);
 6
 7 Console.WriteLine("Customer object state: {0}", ose.State); // Unchanged
 8
 9 customer.Country = "UK";
10
11 Console.WriteLine("Customer object state: {0}", ose.State); // Still Unchanged

  在这个例子中,Customer是个纯POCO类型,跟基于EntityObjectIPOCO的实体不一样,对该实体做变动并不自动与状态管理器保持同步,因为在你的纯POCO实体与实体框架间没有自动的通知机制。所以,在查询状态管理器时,它会认为你的客户对象的状态是Unchanged(未被改动),尽管我们显式地对该实体的一个属性做了变动。

  如果你调用SaveChanges而不选择acceptChangesDuringSave(在保存后接受变动之选项)的话,你会看到在保存后,其状态变成了Modified(改动过了)。这是因为在保存时,基于快照的变动跟踪机制开始起作用,检测到了变动。当然,默认的SaveChanges调用会将状态变回到 Unchanged ,因为默认的Save行为是在保存后接受变动。

  在后面一点,我们将进一步讨论在你需要让你的对象,对象图以及状态管理器间保持同步时该怎么做,但让我们先来看一下可为你所用的POCO的另一类变动跟踪机制。

使用代理的基于通知的变动跟踪

  如果你在乎在你对实体值,关系和对象图做变动时的非常高效的和即时性的变动跟踪的话,这是个另样的方案,基于代理的变动跟踪。如果你把某个特定实体类型上的所有映射属性都声明为virtual的话,你就可以利用基于代理的变动跟踪了。

  使用代理来跟踪变动的实体总是与实体框架的对象状态管理器保持同步,因为代理会在实体的值和关系变动时通知实体框架。总的来说,这使得变动跟踪更为有效,因为对象状态管理器可以略去比较属性的原始值和当前值这一步,如果它知道属性没有变动的话。

  实际上,你从代理上得到的变动跟踪行为跟从基于EntityObject的非POCO实体或IPOCO实体上得到的变动跟踪行为是完全一样的。

  让我们用同一个例子来看一下:

 1 Customer customer = (from c in context.Customers
 2                      where c.CustomerID == "ALFKI"
 3                      select c).Single();
 4
 5 ObjectStateEntry ose = context.ObjectStateManager.GetObjectStateEntry(customer);
 6
 7 Console.WriteLine("Customer object state: {0}", ose.State); // Unchanged
 8
 9 customer.Country = "UK";
10
11 Console.WriteLine("Customer object state: {0}", ose.State); // Modified

  这个例子是不言自明的,在你对实体做变动时,对象状态管理器被通知到你的变动了。在调用SaveChanges时,不会导致另外的花销(overhead)。

  但是,基于代理的变动跟踪也意味着你的实体的运行时类型跟你定义的类型不完全一样,而是你的类型的子类。这在许多场景下(譬如序列化)也许不太合适,你需要选择在你的应用和领域的需求和约束下最合适的方法。

不使用代理,与状态管理器保持同步

  两种方法都有其优缺点,但不是基于代理的纯POCO方案,其之简明和优雅也意味着它会成为你们中许多人的默认选择,也就毫不奇怪了。所以让我们来看一下在这种模式下,如何使状态管理器与你的对象图保持同步。

ObjectContext.DetectChanges()

  在你使用基于快照的纯POCO实体时,有一个至关重要的方法:ObjectContext.DetectChanges()

  这个API在你无论何时改变对象图时都应该显式调用,它会告知状态管理器它需要与的你对象图做同步。

  ObjectContext.SaveChanges在默认情形下会隐式调用DetectChanges,所以,如果你所做的就是对你的对象们做一系列的变动,然后立刻调用Save的话,你不必显式调用DetectChanges。但是,要记住的是,取决于你的对象图的大小,DetectChanges也许会花销很大(而且,取决于你在做什么,DetectChanges也许是多余的),所以,有可能你可以略去SaveChanges中隐式调用的DetectChanges,这一点,在我们讨论Entity Framework 4.0中新引进的SaveChanges的重载方法时会做更多的讨论。

  让我们来看一下DetectChanges如何影响我们前面看过的例子:

 1 Customer customer = (from c in context.Customers
 2                      where c.CustomerID == "ALFKI"
 3                      select c).Single();
 4
 5 ObjectStateEntry ose = context.ObjectStateManager.GetObjectStateEntry(customer);
 6
 7 Console.WriteLine("Customer object state: {0}", ose.State); // Unchanged
 8
 9 customer.Country = "UK";
10 Console.WriteLine("Customer object state: {0}", ose.State); // Still Unchanged
11
12 context.DetectChanges();
13 Console.WriteLine("Customer object state: {0}", ose.State); // Modified

  显式调用DetectChanges 会导致状态管理器与你的对象的状态保持一致。

  因为DetectChanges的潜在花销,依赖于状态管理器与对象图保持一致的ObjectContext上的其他一些APIs是不显式调用它的。因此,无论什么时候你在context上做依赖于状态的操作时,你需要调用DetectChanges

  Object Servces(对象服务)中的下列API的行为取决于ObjectStateManager的当前状态,因此,如果它们操作的部分对象图的情况与对象图的实际状态不同步的话,会受影响:

  ObjectContext API:

  • AddObject
  • Attach
  • AttachTo
  • DeleteObject
  • Detach
  • GetObjectByKey
  • TryGetObjectByKey
  • ApplyCurrentValues
  • ApplyPropertyChanges
  • ApplyOriginalValues
  • Refresh
  • ExecuteQuery

  ObjectStateManager API:

  • ChangeObjectState
  • ChangeRelationshipState
  • GetObjectStateEntry
  • TryGetObjectStateEntry
  • GetObjectStateEntries

  ObjectStateEntry API:

  • Any

  EntityCollection/EntityReference API:

  • Any

SaveChanges的新重载方法

  在Entity Framework 3.5中, 在调用ObjectContext.SaveChanges()时,有两种可能:

  • SaveChanges() – 无参数选项,允许你保存变动,同时隐式接受变动。
  • SaveChanges(bool acceptChangesDuringSave) – 这个选项允许你跳出默认的“accept changes during save”(保存时同时接受变动)行为,你需要在保存后显式地使用AcceptAllChanges来接受变动。

  在加了DetectChanges行为后,再加另外的重载方法很明显会搞乱API。为了解决这个问题,引进了SaveOptions这个列举标记:

1 [Flags]
2 public enum SaveOptions
3 {
4     None = 0,
5     DetectChangesBeforeSave = 1,
6     AcceptChangesAfterSave = 2,
7 }

  SaveChanges现在包括了象这样一个重载:

   1 public virtual int SaveChanges(SaveOptions options); 

  SaveOptions将允许我们(Entity Framework开发团队)以可控制得多的方式来扩展API,假如我们需要在将来添加另外的选项的话。

  但这个重载的有趣性还有另外一个原因:注意,这个SaveChanges重载是virtual。在Entity Framework 4.0中,你可以用你自己的定制行为,作为你选择编写的ObjectContext的继承类的一部分,来覆盖SaveChanges行为。无论你是否使用Entity Framework中的POCO支持,这将允许你以一种Entity Framework 3.5中不可能的方式来做定制。

  至此,我想要重点阐述的Entity Framework 4.0中有关POCO的事情几乎都说完了,请接着期待下一篇在Entity Framework中使用模式的贴子。

  Faisal Mohamood  Entity Framework开发团队的Program Manager

时间: 2024-11-03 23:02:08

实体框架中的POCO支持 - 第三部分 - POCO的变动跟踪的相关文章

实体框架中的POCO支持 - 第一部分 - 体验

[译者按] Entity Framework 1.0 发布也有一段时间了,但感觉用的人很少.其中一个很大的原因,也许就是不支持POCO.要知道,Entity Framework 1.0的做法是让你的实体从EF的基类继承而来,这对很多人,特别是崇尚DDD的人来说,那是一副难以下咽的药啊.曾有微软开发人员提供了一个 POCO Adapter,但那究竟不是正规的做法.Visual Studio 2010 和 .NET 4.0 提供了许许多多的新特性,真是让人激动,向往,大有一种回到.NET 1.0 刚

Entity Framework学习-实体框架中的code-first迁移

.net开发在涉及到操作数据库时,特别是访问SQL SERVER数据库时,经常需要使用一些ORM框架,最常用,且功能很强大的要数EF了.在使用EF进行涉及数据库的开发时,一般会涉及两种模式:1,DB first;2,Code first.相比前者,Code first比较灵活,适合敏捷开发,特别是数据库表结构经常变动的情况.在使用Code first时,经常会碰到对实体类的改动与数据库同步的问题,这时候我们就需要使用code first中的迁移功能,具体可以参考该文:https://msdn.m

关于EF实体框架中的 dbContext

EF4.1包括Code First和DbContext API.DbContext API为EF提供更多的工作方式:Code First,Database First和Model First. 使用DbContext构造函数 1. Code First约定连接 namespace Magic.Unicorn{    public class UnicornsContext : DbContext    {        public UnicornsContext()        // C#

实体框架6.0(Recipes)翻译系列 1 -----第一章 开始使用实体框架1

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一是督促自

《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述 (转)

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>第二版,慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一

Entity Framework 学习总结之一:ADO.NET 实体框架概述

http://www.cnblogs.com/xlovey/archive/2011/01/03/1924800.html ADO.NET 实体框架概述 新版本中的 ADO.NET 以新实体框架为特色.它使开发人员可以通过对象模型(而不是逻辑/关系数据模型)专注于数据.实体框架有助于将逻辑数据架构抽象为概念模型,并且允许以多种方式通过对象服务和名为“EntityClient”的新数据提供程序与概念模型交互. 实体框架使用概念层(ConceptualModels).映射层(Mappings)和逻辑

实体框架 5 性能注意事项

1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便捷方式.对于 .NET 应用程序,Microsoft 推荐的 O/RM 是实体框架.但任何抽象都要考虑性能. 本白皮书旨在介绍在使用实体框架开发应用程序时的性能注意事项,使开发人员了解能够影响性能的实体框架内部算法,以及提供有关进行调查及在使用实体框架的应用程序中提高性能的提示.网络上有大量很好的有关性能的主题,我们还尽可能地指出这些资源的链接. 性能是一个很微妙的主题.对于使用实体框架的应用程序,可将本白皮书作为资源来帮

Entity Framework实体框架使用TrackerEnabledDbContext进行操作日志跟踪

在EF实体框架中进行日志跟踪,一般都是自己写个Log实体类,在数据保存时进行属性原始值验证来进行日志跟踪.当然还可以使用一些第三扩展库例如:entity framework extended进行日志记录,本文介绍如何使用TrackerEnabledDbContext进行操作日志跟踪. 1,首先创建项目工程,TrackerEnabledDbContext只支持4.5及以上框架,详情见:https://www.nuget.org/packages/TrackerEnabledDbContext 2,

Rafy 领域实体框架 - 树型实体功能(自关联表)

  在 Rafy 领域实体框架中,对自关联的实体结构做了特殊的处理,下面对这一功能进行讲解.   场景 在开发数据库应用程序时,往往会遇到自关联表的场景.例如,分类信息.组织架构中的部门.文件夹信息等,都是不限制层级的.如下图中操作系统的文件夹: 在开发这类程序时,往往是设计一张表,表中的一个可空的外键直接引用这张表本身.对应的实体如下图: 而针对这样的场景,许多ORM框架都不做默认的处理,开发者往往每次都要做重复的工作:建立类似结构的表,编写关系处理代码,编写查询代码--而这种场景经常会出现,