实体框架 - 并发


任何数据访问开发人员在回答有关数据并发性的问题时都会遇到困难:“如果多个人同时编辑相同的数据会发生什么?”

  • 我们当中比较幸运的人所遵循的商业规则是“没问题,最后一个获胜”。

  • 在这种情况下,并发不是问题。更有可能的是,事情并没有那么简单,并且没有灵丹妙药可以同时解决所有情况。

  • 默认情况下,实体框架将采用“胜利中的最后一个”路径,这意味着即使其他人在检索数据的时间和保存数据的时间之间更新了数据,也会应用最新的更新。

让我们举一个例子来更好地理解它。以下示例在 Course 表中添加一个新列 VersionNo。

课程表

转到设计器并右键单击设计器窗口并选择从数据库更新模型...

设计师

您将看到课程实体中添加了另一列。

课程实体

右键单击新创建的列VersionNo,然后选择Properties,并将ConcurrencyMode更改为Fixed,如下图所示。

新创建的专栏

将 Course.VersionNo 的 ConcurrencyMode 设置为固定后,每当更新课程时,Update 命令都会使用其 EntityKey 和 VersionNo 属性查找课程。

让我们看一个简单的场景。两个用户同时检索同一门课程,用户 1 将该课程的标题更改为数学,并在用户 2 之前保存更改。稍后,当用户 2 更改在用户 1 保存其更改之前检索到的该课程的标题时,即如果用户 2 会收到并发异常“User2: 乐观并发异常发生”

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         Course c1 = null;
         Course c2 = null;

         //User 1 gets Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 2 also get the same Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 1 updates Course Title
         c1.Title = "Edited from user1";

         //User 2 updates Course Title
         c2.Title = "Edited from user2";

         //User 1 saves changes first

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c1).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User1: Optimistic Concurrency exception occurred");
            }
         }

         //User 2 saves changes after User 1.
         //User 2 will get concurrency exection
         //because CreateOrModifiedDate is different in the database

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c2).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User2: Optimistic Concurrency exception occurred");
            }
         }
      }
   }
}