Entity FrameWork乐观并发模式

来源:互联网 发布:达州网络人才网 编辑:程序博客网 时间:2024/06/06 09:40

乐观并发关系到乐观的尝试将entity保存到数据库时,希望entity数据在读取之后,没有被改变过。如果数据被改变过,将会抛出一个异常,而你必须在尝试再次保存前解决这个冲突。本篇涵盖了如何在EntityFrameWork中处理这样的异常。本篇涉及的技术在对于使用Code First或是EF Desinger来创建模型时是同样适用的

本篇并不适合讨论乐观并发,后续内容中假定读者有一定的并发解决方案和通常任务展示模式的知识。

当你使用非关系型数据库(外键并不映射到entity上的一个属性),比使用外键关联时来解决并发问题要困难的多。因此如果你准备实现并发解决方案建议你的应用在你的entity中使用外键。后续所有的例子都假定你使用外键关联

当SaveChanges尝试将一个使用外键关联的entity保存时,检测到一个乐观并发异常时,会抛出一个DbUpdateConcurrencyException异常

数据库优先,使用Reload来解决乐观并发异常

Reload方法用数据库中的值来覆写entity的当前值。entity将会以某种形式会给用户,让他们再次进行改动,并重新保存,例如

using (var context = new BloggingContext()) {     var blog = context.Blogs.Find(1);     blog.Name = "The New ADO.NET Blog";     bool saveFailed;     do     {         saveFailed = false;         try         {             context.SaveChanges();         }         catch (DbUpdateConcurrencyException ex)         {             saveFailed = true;             // Update the values of the entity that failed to save from the store             ex.Entries.Single().Reload();         }     } while (saveFailed); }

模拟并发异常的一个好的方法是在SaveChanges上设置一个断点,然后使用其他数据库工具修改一个entity并保存。你也可以在SaveChanges之前插入一行直接更新数据库的SqlCommand,例如

context.Database.SqlCommand("UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");

DbUpdateConcurrencyException上的Entries方法返回更新失败的entities的DbEntityEntry的实例。这个方法当前对于并发问题是返回单一的值,它也可能返回多个更新异常的值。一个可选的方案就是获取所有的entities,并为他们每一个都执行Reload

以用户优先来解决乐观并发

上面的例子中使用数据库优先,或是称为存储优先的Reload方法,因为在entity中的值被来自于数据的值覆盖。有些时候你可能希望的行为正好相反,使用当前entity中的值来覆盖数据库中的值。这称为用户优先的方式是通过获取当前数据库中的值,并将他们设为entity的original值来实现的

using (var context = new BloggingContext()) {     var blog = context.Blogs.Find(1);     blog.Name = "The New ADO.NET Blog";     bool saveFailed;     do     {         saveFailed = false;         try         {             context.SaveChanges();         }         catch (DbUpdateConcurrencyException ex)         {             saveFailed = true;             // Update original values from the database             var entry = ex.Entries.Single();             entry.OriginalValues.SetValues(entry.GetDatabaseValues());         }     } while (saveFailed); }

Sometimes you may want to combine the values currently in the database with the values currently in the entity. This usually requires some custom logic or user interaction. For example, you might present a form to the user containing the current values, the values in the database, and a default set of resolved values. The user would then edit the resolved values as necessary and it would be these resolved values that get saved to the database. This can be done using the DbPropertyValues objects returned from CurrentValues and GetDatabaseValues on the entity’s entry. For example:

自定义乐观并发异常解决方案

有时候你希望混合当前数据库和当前entity的值。这通常需要一些自定义的逻辑或是界面,例如,你可能要使用表单来呈现包含当前值,数据库值,最终值给用户。用户需要编辑最终值,并将最终值存入数据库。这个步骤可以在entity上使用GetDatabaseValues或是CurrentValues返回的DbPropertyValues对象来完成。例如

using (var context = new BloggingContext()) {     var blog = context.Blogs.Find(1);     blog.Name = "The New ADO.NET Blog";     bool saveFailed;     do     {         saveFailed = false;         try         {             context.SaveChanges();         }         catch (DbUpdateConcurrencyException ex)         {             saveFailed = true;             // Get the current entity values and the values in the database             var entry = ex.Entries.Single();             var currentValues = entry.CurrentValues;             var databaseValues = entry.GetDatabaseValues();             // Choose an initial set of resolved values. In this case we             // make the default be the values currently in the database.             var resolvedValues = databaseValues.Clone();             // Have the user choose what the resolved values should be             HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);             // Update the original values with the database values and             // the current values with whatever the user choose.             entry.OriginalValues.SetValues(databaseValues);             entry.CurrentValues.SetValues(resolvedValues);         }     } while (saveFailed); } public void HaveUserResolveConcurrency(DbPropertyValues currentValues,                                        DbPropertyValues databaseValues,                                        DbPropertyValues resolvedValues) {     // Show the current, database, and resolved values to the user and have     // them edit the resolved values to get the correct resolution. }

使用object来自定义乐观并发异常解决方案

上述代码使用DbPropertyValues 实例来传递当前值,数据库值,和最终值。更方便的方法是使用你的entity实例类型来完成。使用DbPropertyValues的ToObject和SetValues来完成,比如

using (var context = new BloggingContext()) {     var blog = context.Blogs.Find(1);     blog.Name = "The New ADO.NET Blog";     bool saveFailed;     do     {         saveFailed = false;         try         {             context.SaveChanges();         }         catch (DbUpdateConcurrencyException ex)         {             saveFailed = true;             // Get the current entity values and the values in the database             // as instances of the entity type             var entry = ex.Entries.Single();             var databaseValues = entry.GetDatabaseValues();             var databaseValuesAsBlog = (Blog)databaseValues.ToObject();             // Choose an initial set of resolved values. In this case we             // make the default be the values currently in the database.             var resolvedValuesAsBlog = (Blog)databaseValues.ToObject();             // Have the user choose what the resolved values should be             HaveUserResolveConcurrency((Blog)entry.Entity,                                        databaseValuesAsBlog,                                        resolvedValuesAsBlog);             // Update the original values with the database values and             // the current values with whatever the user choose.             entry.OriginalValues.SetValues(databaseValues);             entry.CurrentValues.SetValues(resolvedValuesAsBlog);         }     } while (saveFailed); } public void HaveUserResolveConcurrency(Blog entity,                                        Blog databaseValues,                                        Blog resolvedValues) {     // Show the current, database, and resolved values to the user and have     // them update the resolved values to get the correct resolution. }

原文地址