实体框架 - 并发

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

  • 我们当中比较幸运的人会处理这样的业务规则:"没问题,最后一个人获胜。"

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

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

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

Course Table

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

Designer

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

Course Entity

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

New Created Column

使用 ConcurrencyMode Course.VersionNo 设置为 Fixed,每次更新 Course 时,Update 命令都会使用其 EntityKey 和 VersionNo 属性查找 Course。

让我们看一个简单的场景。两个用户同时检索同一门课程,用户 1 将该课程的标题更改为数学并在用户 2 之前保存更改。稍后,当用户 2 更改在用户 1 保存更改之前检索到的该课程的标题时,在这种情况下,用户 2 将收到并发异常 "用户 2:发生了乐观并发异常"

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");
            }
         }
      }
   }
}