2011-02-14 58 views
3

讓我們想象我有兩個線程,它們在具有ReadCommitted隔離級別的特定於線程的TransactionScopes中執行一些面向數據庫的代碼。但是有一些表格應該共享數據(不應該創建重複數據)。TransactionScope細微差別

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted)) 
{ 
    ...//some code 
    if (!_someRepository.IsValueExists(value)) 
     _someRepository.AddData(value); 
    ...//some code 
    transactionScope.Complete(); 
} 

的問題是兩個線程可以檢查是否在差不多同一時間存在的數據,如果沒有 - 創建複製數據(約束不會在這裏幫助:我有防止意外情況發生)。我想這是一個微不足道的問題,但它通常如何解決?

我看到下面的示意性的解決方案:

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted)) 
{ 
    ...//some code 
    transactionScope.IsolationLevel = IsolationLevel.ReadUncommitted; //change Isolation Level 
    lock (_sharedDataLockObject) 
    { 
     if (!_someRepository.IsValueExists(value)) 
     _someRepository.AddData(value); 
    } 
    transactionScope.IsolationLevel = IsolationLevel.ReadCommitted; //reset IsolationLevel 
    ...//some code 
    transactionScope.Complete(); 
} 

這種解決方案的第一個問題是,TransactionScope的不支持的IsolationLevel修改。但讓我們想象我在這裏使用ADO.NET事務。不過,我不確定它是否有效。

+0

`快照`隔離級別是否有助於您的情況?這個級別減少了複製修改行時的鎖定。這樣,其他事務仍然可以讀取舊數據,而無需等待解鎖。 – whyleee 2011-02-14 20:25:15

+0

@whyleee:但我的問題是添加記錄,但沒有修改 – SiberianGuy 2011-02-14 20:37:18

+0

但是,如果你需要一個鎖,`ReadCommitted`隔離級鎖定來自其他事務的寫鎖的記錄。 – whyleee 2011-02-14 20:44:56

回答

1

我通常解決與DB約束完全相同的問題,並通過在catch塊中重試try-catch包裝整個事務。當然,如果由於某種原因無法重新啓動交易,這不是一個有效的解決方案(例如,交易是在您的控制之外開始的 - 我不確定您的意思,「我必須防止發生異常情況「)。

取決於交易通常需要多長時間,您可能需要稍等一會才能重試或進行幾次重試,但只要並行事務成功完成,您的重試將成功。

棘手的問題通常是如何確定異常是由特定的約束違規引起的。這通常需要一些經驗性測試來確定確切的異常類型和異常消息的一些醜陋的字符串匹配。

2

在這種情況下,我會做一個雙重檢查。

首先檢查它不存在,不需要在這裏進行交易。

然後開始一個可序列化的事務。

檢查,它仍然如果不存在添加

提交併關閉交易不存在