Khung thực thể - Đồng tiền
Bất kỳ nhà phát triển quyền truy cập dữ liệu nào cũng gặp khó khăn khi trả lời câu hỏi liên quan đến tính đồng thời của dữ liệu, "Điều gì sẽ xảy ra nếu nhiều người đang chỉnh sửa cùng một dữ liệu cùng một lúc?"
Những người may mắn hơn trong chúng ta đối phó với các quy tắc kinh doanh nói rằng "không có vấn đề, cuối cùng là chiến thắng."
Trong trường hợp này, đồng thời không phải là một vấn đề. Nhiều khả năng, nó không đơn giản như vậy và không có viên đạn bạc nào để giải quyết mọi tình huống cùng một lúc.
Theo mặc định, Entity Framework sẽ sử dụng đường dẫn "người cuối cùng thắng", có nghĩa là bản cập nhật mới nhất được áp dụng ngay cả khi người khác cập nhật dữ liệu giữa thời gian dữ liệu được truy xuất và dữ liệu thời gian được lưu.
Hãy lấy một ví dụ để hiểu rõ hơn. Ví dụ sau thêm một cột mới VersionNo trong bảng Course.
Đi tới 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 mô hình cập nhật từ cơ sở dữ liệu…
Bạn sẽ thấy rằng một cột khác được thêm vào Thực thể khóa học.
Nhấp chuột phải vào cột VersionNo mới tạo và chọn Thuộc tính và thay đổi ConcurrencyMode thành Fixed như trong hình sau.
Với ConcurrencyMode of Course.VersionNo được đặt thành Fixed, bất cứ khi nào một Khóa học được cập nhật, lệnh Cập nhật sẽ tìm kiếm Khóa học bằng cách sử dụng EntityKey và thuộc tính VersionNo của nó.
Hãy xem một kịch bản đơn giản. Hai người dùng truy xuất cùng một khóa học cùng lúc 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 trước người dùng 2. Sau đó, khi người dùng 2 thay đổi tiêu đề của khóa học đó đã được truy xuất trước khi người dùng 1 lưu các thay đổi của mình, trong đó trường hợp người dùng 2 sẽ nhận được ngoại lệ đồng thời"User2: 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).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");
}
}
}
}
}