2016-08-29 93 views
0

我在我的webapp中看到一些奇怪的競爭條件,我懷疑這可能是由於Entity Framework以意外的方式處理讀鎖。當我的應用程序向任何頁面發出請求時,我會自動加載帳戶模型,然後將其存儲在我的DbContext中,以保存請求的整個生命週期。某些網頁需要鎖定帳戶DB行,以便在沒有競爭條件的情況下安全地執行其他操作。下面是我現在怎麼做這個...實體框架事務鎖定

//... code that begins the request and loads the account into context. 
// Some pages may run code that looks something like this. 
using(var tran = existingCtx.Database.BeginTransaction(IsolationLevel.RepeatableRead)) 
{ 
    // Lock customer. 
    var act = ctx.Accounts.Find(purchaseFor.ID); 
    if (act == null) 
     throw new RecordNotFoundException("Unable to find specified customer."); 

    DoStuffRelyingOnLock(); 
    Commit(); 
} 

會將呼叫查找查找(purchaseFor.ID)鎖定在數據庫中的帳戶行,即使它已經加載到背景?

回答

2

請求調用Find(purchaseFor.ID)LOCK數據庫中的帳戶行,即使它已經加載到上下文中?

不,它不會。如果上下文已經加載了該實體,則它甚至不會與數據庫進行通信。

見什麼documentation說有關Find方法的工作原理(重點煤礦):

使用主鍵

DbSetFind方法使用的主鍵值來試圖尋找實體找到由上下文跟蹤的實體。 如果在上下文中找不到該實體,則會將查詢發送到數據庫以在那裏查找實體。如果在上下文或數據庫中找不到實體,則返回空值。

Find是從兩個顯著方式使用查詢不同:

  • 一個往返到數據庫中,如果與給定鍵的實體未在上下文中找到纔會進行。
  • Find將返回處於Added狀態的實體。也就是說,Find將返回已添加到上下文但尚未保存到數據庫的實體。

因此,如果要確保查詢的數據庫用於鎖定的目的總是爲,避免使用Find,並使用LINQ Where方法來代替。

2

這是很好的做法,如果你要明確地處理事務,你有你的代碼要麼提交回滾。 MSDN documentation指示顯式回滾是必需的,但using語句將觸發Dispose()方法,該方法反過來將回滾所有打開的事務。爲了清楚起見,特別是當代碼變得更復雜時,最好明確地處理回滾。

這個OpenStack question在接受的答案中有一個更透徹的解釋。

+2

是否沒有隱式回滾?如果沒有,那麼實現'IDisposable'的事務有什麼意義呢? – Blorgbeard

+0

請參閱mdsn文檔[鏈接](https://msdn.microsoft.com/en-us/data/dn456843.aspx)以查看EF上下文中的using語句和正確的事務處理。 – btberry

+1

是的,你所建議的是好的做法。但是,說OP的代碼會讓事務處於開放狀態是完全錯誤的。事務實例將被放置在'using'塊的末尾。這包括如果當時交易仍然開放,則調用回滾。 – sstan