通过数据绑定修改数据
“实体框架”提供了与数据库的双向通信通道。前面已经讲述了如何使用“实体框架”获 取数据,现在来看看如何修改获取的信息,并将改动发送回数据库。
26.2.1 更新现有数据
使用一个 ObjectContext 对象获取数据时,根据数据创建的对象位于应用程序的内存缓存中。 为了更改缓存中的对象的值,采取的方式和修改任何普通对象中的值一样——设置它们的 属性。然而,更新内存中的对象不会更新数据库。要在数据库中反映出这个改动(换言之, 将改动“持久化”到数据库中),需要生成恰当的 SQL UPDATE 命令,并指示数据库服务器 执行这些命令。使用“实体框架”可以轻松做到这一点。以下代码展示了一个 LINQ to Entities 查询,它获取 ID 为 14 的产品,将它的名称更改为"Bean Curd"(产品 14 最初在 Northwind 数据库中命名为"Tofu"),再将更改发送回数据库。
NorthwindEntities northwindContext = new NorthwindEntities();
Product product = northwindContext.Products.Single(p => p.ProductID == 14);
product.ProductName = "Bean Curd";
northwindContext.SaveChanges();
在上述代码中,关键语句是对 ObjectContext 对象的 SaveChanges 方法的调用。(记住, NorthwindEntities 派生自 ObjectContext。)对通过运行查询来填充的一个实体对象中的信息 进行修改时,对连接(原始的查询就是在这个连接上运行的)进行管理的 ObjectContext 对象 会跟踪数据发生的改变。SaveChanges 方法会将这些改变传回数据库。在幕后,ObjectContext 对象会构建并运行一个 SQL UPDATE 语句。
如果获取并修改了几个产品,只需在最后一次修改之后调用一次 SaveChanges 就可以了。 SaveChanges 方法一次性提交所有更新。ObjectContext 对象创建一个数据库事务处理 (database transaction),并在这个事务处理中执行所有 SQL UPDATE 语句。任何一个更新失败, 都 会 取 消 事 务 处 理 —— SaveChanges 方 法 在 数 据 库 中 进 行 的 所 有 更 改 都 会 回 滚 , SaveChanges 方法最后将抛出一个异常。如果所有更新成功,事务处理就生效,所有更改都 在数据库中变成永久性的更改。注意,假如 SaveChanges 方法失败,只有数据库才会回滚,
对内存中的实体对象进行的更改仍然存在。SaveChanges 失败时抛出的异常会提供与失败原
因有关的一些信息。可以根据这些信息来修正错误,并再次调用 SaveChanges。
ObjectContext 类还提供了 Refresh 方法。使用这个方法,可以利用数据库中的数据来重新填 充缓存中的 EntityObject 集合,并放弃进行的任何更改。要像下面这样使用该方法:
northwindContext.Refresh(RefreshMode.StoreWins, northwindContext.Products);
第 一 个 参 数 是 System.Data.Objects.RefreshMode 枚 举 的 一 个 成 员 , 如 果 指 定 的 值 是 RefreshMode. StoreWins,就会强迫用数据库中的数据来进行刷新。第二个参数是缓存中要 要刷新的实体。
提示 对更改进行跟踪是 ObjectContext 对象执行的一个代价不菲的操作。如果确定以 后 不 需 要 修 改 数 据 ( 例 如 , 假 定 程 序 生 成 的 是 一 份 只 读 的 报 表 ) , 就 可 以 将 MergeOption 属性设为 MergeOption.NoTracking,从而禁止对 EntityObject 的更改 进行跟踪。如下所示:
northwindContext.Suppliers.MergeOption = MergeOption.NoTracking;
可对一个禁止了?更改跟踪?的实体进行更改,但在调用 SaveChanges 时,这些更改不会 保存。应用程序退出时,它们会丢失。
26.2.2 处理冲突的更新
一个更新操作可能出于多种原因而失败,其中最常见的原因就是发生了冲突。两个用户试 图同时更新相同的数据,就会发生冲突。仔细思考一下在应用程序中使用“实体框架”的 后果,就会发现其实有许多地方都可能发生冲突。通过一个 ObjectContext 对象获取数据时, 这些数据被缓存应用程序的内存中。与此同时,另一个用户执行相同的查询,并获取相同 的数据。如果两个人都修改了数据,而且都调用了 SaveChanges 方法,那么在数据库中, 一个人就会覆盖另一个人的修改。这种现象称为 lost update。
之所以发生这种现象,是由于“实体框架”实现了“开发式并发”(optimistic concurrency)。 换言之,当它从数据库获取数据时,不会在数据库中锁定那些数据。这种形式的并发性允 许其他用户同时访问相同的数据,但它假定两个用户同时更改相同数据的机率非常小(这 正是“开发式并发”一词的来历59)。
与开放式并发相反的是“封闭式并发”(pessimistic concurrency)。在这个方案中,所有数据 都在获取时锁定,其他用户不能同时访问它们。这样一来,就保证了不会丢失任何更改。 但是,这个方案过于极端。
59 “开放式并发”原文是“optimistic concurrency”,这里只是按照 Microsoft 的文档把它翻译为“开放式并发”。其实,更贴 切的翻译是“乐观并发”。对应地,“封闭式并发”(pessimistic concurrency)更贴切的翻译是“悲观并发”。——译者注