Entity Framework 4.1 之六:樂觀并發(fā)
原文名稱:Entity Framework 4.1: Optimistic Concurrency (6)
原文地址:http://vincentlauzon.wordpress.com/2011/04/17/entity-framework-4-1-optimistic-concurrency-6/
這篇文章討論樂觀并發(fā)。
我們經(jīng)常要面對多用戶并發(fā)訪問問題。這是因為人訪問機器的速度無法與高速的機器匹配,典型情況下,人需要大約一分鐘或者更多來填寫數(shù)據(jù),而機器的交易處理通常只需要不到一秒鐘。
在用戶編輯相應(yīng)數(shù)據(jù)的時候,我們不能讓數(shù)據(jù)庫的事務(wù)處理保持打開,這有許多原因:連接問題,信任問題,技術(shù)原因等等。基于這些原因,我們需要并發(fā)管理。有兩種管理方法:樂觀和悲觀。悲觀方式更難處理,因為你需要實現(xiàn)基于記錄的鎖定機制,而且還隨之而來帶來一些問題,例如,在自動釋放鎖之前,系統(tǒng)應(yīng)該鎖定多長的時間。樂觀并發(fā)要簡單一些。
樂觀并發(fā)基于的一個基本的假設(shè)是用戶的修改很少沖突。對于很少用戶同時編輯同樣數(shù)據(jù)的應(yīng)用來說,不管有很多用戶還是很少的用戶都是成立的。基本的考慮是為訪問數(shù)據(jù)的用戶提供一個數(shù)據(jù)的版本,當用戶以后回來更新數(shù)據(jù)的時候,通過版本來確認原來的數(shù)據(jù),如果數(shù)據(jù)已經(jīng)在后臺被其他操作更新,版本發(fā)生了變化,我們就可以通過版本檢測到,然后拒絕更新。
可以有許多方式來實現(xiàn)版本,包括通過一個版本數(shù)字來關(guān)聯(lián)到數(shù)據(jù),可以是最后一次修改的日期時間字段,還可能是多于一個的字段。在 EF 中,這被稱為并發(fā)標識 concurrenty token,在這篇文章中,我使用 SQL Server 的 time-stamp 特性,這需要在表中增加一個 time-stamp 類型的列,我們通過它來實現(xiàn)樂觀并發(fā)。由 SQL Server 在每次記錄被更新的時候維護這個列。
為了告訴 EF 在實體中有一個屬性表示并發(fā)標識,你可以通過標簽 [ConcurrencyCheck] 來標識這個屬性,或者使用模型構(gòu)建器。我認為并發(fā)標識定義了業(yè)務(wù)規(guī)則,應(yīng)該是模型的一部分。所以這里使用標簽。
public class Order
{
public int OrderID { get; set; }
[Required]
[StringLength(32, MinimumLength = 2)]
public string OrderTitle { get; set; }
[Required]
[StringLength(64, MinimumLength=5)]
public string CustomerName { get; set; }
public DateTime TransactionDate { get; set; }
[ConcurrencyCheck]
[Timestamp]
public byte[] TimeStamp { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
public virtual List<Employee> InvolvedEmployees { get; set; }
}
在這段代碼中,當我們通過 DbContext 調(diào)用 SaveChanges 的時候,將會使用樂觀并發(fā)。
Timestamp 屬性的類型是 byte[], 通過標簽 Timestamp ,將這個屬性映射到 SQL Server 的 time-stamp 類型的列。
現(xiàn)在,我們模擬并發(fā),我們將執(zhí)行下面的三個步驟:
- 創(chuàng)建一個訂單
- 模擬用戶 X 修改這個訂單
- 模擬用戶 Y 修改這個訂單,此時訂單已經(jīng)被修改,而用戶 Y 不知道。
通過在另一個 DbContext 中連接實體來模擬修改的過程,當我們通過 DbContet 連接實體的時候,它會假設(shè)實體沒有被修改,所以我們在它之后進行修改。
private static void ConcurrencyCheck()
{
Order originalOrder;
// Create an order
using (var context1 = new MyDomainContext())
{
originalOrder = new Order
{
OrderTitle = "Paper",
CustomerName = "*Bob*",
TransactionDate = DateTime.Now
};
context1.Orders.Add(originalOrder);
context1.SaveChanges();
}
// Simulate the modification of the created order by user X
using (var context2 = new MyDomainContext())
{ // Recreate the order object in order to attach it
var order = new Order
{
OrderID = originalOrder.OrderID,
OrderTitle = originalOrder.OrderTitle,
CustomerName = originalOrder.CustomerName,
TransactionDate = originalOrder.TransactionDate,
TimeStamp = originalOrder.TimeStamp
};
context2.Orders.Attach(order);
// Alter the order
order.CustomerName = "Robert";
context2.SaveChanges();
}
// Simulate the modification of the created order by user Y (after user X already modified it)
using (var context3 = new MyDomainContext())
{ // Recreate the order in order to attach it
var order = new Order
{
OrderID = originalOrder.OrderID,
OrderTitle = originalOrder.OrderTitle,
CustomerName = originalOrder.CustomerName,
TransactionDate = originalOrder.TransactionDate,
TimeStamp = originalOrder.TimeStamp
};
context3.Orders.Attach(order);
// Alter the order
order.CustomerName = "Luke**";
try
{
context3.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine("Concurrency exception on " + ex.Entries.First().Entity.GetType().Name);
}
}
}
你也可以強制 EF 相信訂單已經(jīng)被修改了。
context3.Entry(order).State = EntityState.Modified;
所以,EF 提供了開箱即用的樂觀并發(fā)支持,這個竅門也可以用在 EF 狀態(tài)的各個方面:當你連接實體的時候,確信它們處于正確的狀態(tài)。
浙公網(wǎng)安備 33010602011771號