設置並發模式
Entity Framework 實現了樂觀的並發模式(Optimistic Concurrency Model)。默認情況下,在實體更新數據提交到數據庫時,並不會檢查並發。對於高頻率的並發屬性,你需要設置屬性的並發模式為Fixed。
這些屬性將會加入到T-SQL腳本的WHERE子句部分,用來比較客戶端的值和數據庫端的值。
示例代碼:
public void UpdateProduct()
{
Product product = context.Product.FirstOrDefault(p => p.ProductID == 1004);
if (product != null)
{
product.Color = "White";
product.StandardCost = 200;
product.ListPrice = 250;
}
context.SaveChanges();
}
正常情況下,不檢查並發情況,產生的T-SQL腳本如下:
exec sp_executesql N'update [SalesLT].[Product]
set [Color] = @0, [StandardCost] = @1, [ListPrice] = @2
where ([ProductID] = @3)',
N'@0 nvarchar(5), @1 decimal(19,4), @2 decimal(19,4), @3 int', @0=N'White', @1=1000.0000, @2=2000.0000,@3=1004
在設置Product實體的Color屬性的Concurrency Mode 為Fixed,你會發現生成的T-SQL腳本中,Where子句中增加了對Color 字段的檢查:
exec sp_executesql N'update [SalesLT].[Product]
set [Color] = @0, [StandardCost] = @1, [ListPrice] = @2
where (([ProductID] = @3) and ([Color] = @4))',
N'@0 nvarchar(5), @1 decimal(19,4), @2 decimal(19,4), @3 int, @4 nvarchar(5)', @0=N'White', @1=200.0000, @2=250.0000, @3=1004, @4=N'White'
解決並發沖突
如下的示例代碼演示如何解決並發沖突。當發生並發沖突時,將拋出OptimisticConcurrencyException 異常,可以通過調用Object Context 的Refresh() 方法,傳入RefreshMode.ClientsWins 選項,來解決沖突。這將重新執行LINQ to Entities 查詢,並覆蓋數據庫的值,隨後再次調用SaveChanges() 方法。
模擬並發沖突及其腳本:
public void UpdateProduct()
{
Product product1 = context.Product.FirstOrDefault(p => p.ProductID == 1004);
if (product1 != null)
{
product1.Color = "Black";
product1.StandardCost = 20;
product1.ListPrice = 25;
}
AdventureWorksLTEntities context2 = new AdventureWorksLTEntities();
Product product2 = context2.Product.FirstOrDefault(p => p.ProductID == 1004);
if (product2 != null)
{
product2.Color = "Blue";
product2.StandardCost = 18;
product2.ListPrice = 30;
}
// Save changes of first DataContext
context.SaveChanges();
try
{
context2.SaveChanges();
}
catch (OptimisticConcurrencyException ex)
{
Console.WriteLine(ex.ToString());
// The concurrently conflict can be resolved by refreshing the DataContext
context2.Refresh(RefreshMode.ClientWins, product2);
// And saving the changes again
context2.SaveChanges();
}
}
Refresh的第一個參數值得注意一下,它是一個枚舉值,有兩個選項:StoreWins或者是ClientWins。如果是StoreWins,那麼,Refresh以後,product2的值將與數據庫裡的對應記錄的值一致(修改會丟失);而如果ClientWins,則product2的值保持,並且提交以後,會把context提交的修改覆蓋。
其實,這兩種方法均不完美,總會導致一部分修改丟失。但是,這總比在不知情的情況下的覆蓋要好。 另外,需要說明,上面的方法,只是對並發沖突的一種模擬,這樣的模式,在處理並發沖突時會有問題。一般的處理方法是,當檢測到並發沖突時,提示用戶會重新從數據庫載入數據,然後,讓用戶在新數據的情況下重新修改後再次提交,直到不再有並發沖突發生。