Concurrency trong Entity Framework

Bất kỳ nhà phát triển nào cũng đã từng gặp khó khăn khi giải quyết các vấn đề liên quan đến truy cập dữ liệu đồng thời. Truy cập dữ liệu đồng thời (concurrency) xảy ra nếu có nhiều hơn một người dùng đang chỉnh sửa cùng một dữ liệu tại cùng một thời điểm.

Mặc định Entity Framework hỗ trợ xử lý truy cập dữ liệu đồng thời (concurrency). Entity Framework sẽ lưu một thông tin phiên bản vào cơ sở dữ liệu, thông tin này sẽ tự động thay đổi mỗi khi bạn thực hiện lệnh update.

Khi Entity Framework thực hiện lệnh update nó sẽ bổ sung thêm thông tin phiên bản vào mệnh đề where của truy vấn. Nếu dữ liệu đã được thay đổi trước đó thì sẽ có một ngoại lệ xảy ra.

Ví dụ sau đây sẽ minh họa cách xử lý truy cập dữ liệu đồng thời bằng cách thêm một cột VersionNo vào bảng Course.

Thêm cột VersionNo vào bảng Course

Đi đến trình thiết kế và nhấp chuột phải vào cửa sổ trình thiết kế và chọn Update Model from Database…

Thêm cột VersionNo vào bảng Course

Bạn sẽ thấy cột VersionNo đã được thêm vào trong thực thể Course.

Thêm cột VersionNo vào bảng Course

Nhấp chuột phải vào cột VersionNo mới được tạo và chọn Properties rồi thay đổi ConcurrencyMode thành Fixed như trong hình dưới đây.

Thêm cột VersionNo vào bảng Course

Sau khi đã thiết lập ConcurrencyMode của VersionNo thành Fixed, bất cứ khi nào Course được cập nhật, lệnh update sẽ tìm Course bằng cách sử dụng thuộc tính EntityKey và thuộc tính VersionNo.

Hãy xem xét một kịch bản đơn giản sau:

  • Hai người dùng cùng truy xuất một khóa học tại cùng một thời điểm và người dùng 1 thay đổi tiêu đề của khóa học đó thành Toán học và lưu các thay đổi vào cơ sở dữ liệu.
  • Sau đó, người dùng 2 thay đổi tiêu đề của khóa học đó thành Hình học rồi lưu vào cơ sở dữ liệu. Trong trường hợp này người dùng 2 sẽ nhận được ngoại lệ "Optimistic Concurrency exception occured" .
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).FirstOrDefault();
         }

         //User 2 also get the same Course
         using (var context = new UniContextEntities()) 
         {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).FirstOrDefault();
         }

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

Concurrency trong Code-First

Bạn có thể sử dụng TimestampAttribute cho thuộc tính VersionNo trong Code-First. Hãy đảm bảo rằng kiểu dữ liệu của VersionNo là kiểu byte[].

[Timestamp]
public byte[] VersionNo { get; set; }

EF sẽ tự động thêm VersionNo vào trong mệnh đề where trong khi thực hiện thao tác update.



Bài viết liên quan:

2 kịch bản lưu dữ liệu trong Entity Framework Core là kịch bản được kết nối và kịch bản ngắt kết nối.

Tạo ứng dụng .NET Core Console đầu tiên và cấu hình sử dụng Entity Framework Core.

Truy vấn trong Entity Framework Core có gì mới? Truy vấn trong EF Core khác EF ở những điểm nào.