2015-04-17 177 views
1

我有一個可以被多個程序訪問的SQL Server數據庫。這些程序用C#編寫,並使用Fluent NHibernate作爲ORM。但是數據庫中存在一些對象(稱爲令牌),根據域邏輯,不應允許多個進程/線程同時處理它們。正確處理StaleObjectStateException

我試圖通過NHIbernate版本鎖定實現此目的。我有以下映射

mapping.OptimisticLock.Version(); 
mapping.Version(token => token.VersionTimestamp).Generated.Always().UnsavedValue(null).Access.Property().CustomSqlType("timestamp").Nullable(); 

和下面的代碼:

var token = Session.Get<Token>(tokenId); 

if (token.Status != TokenStatus.Available) 
    return new FailedResult("Token not available"); 

try 
{ 
    token.Status = TokenStatus.Locked; 
    Session.SaveOrUpdate(token); 
} 
catch (StaleObjectStateException) 
{ 
    WriteLogsToDb(); // <- another StaleObjectStateException thrown here 
    return new FailedResult("Could not acquire token"); 
} 

ProcessToken(token); // do stuff that isn't allowed to be done concurrently by multiple threads/processes 

token.Status = TokenStatus.Available; 
Session.SaveOrUpdate(token); 

return new SuccessResult(); 

的問題是,我得到一個StaleObjectStateException後,我無法保存任何東西到DB(即使是沒有版本的實體映射)。我得到另一個StaleObjectStateException。但是我無法鎖定令牌後,我真的需要在DB中寫入日誌和其他一些東西。

什麼是正確的方法來做到這一點? NHibernate的這種行爲的原因是什麼?

回答

0

會話是UoW,可以合併您在沖洗操作期間所做的所有更改並接受它。因爲在刷新期間(SaveOrUpdate沒有事務執行Flush),你的對象(標記)仍然被標記爲髒,並且NH在下一次刷新期間嘗試保存它,並且如果它仍然鎖定,你又會得到異常。

爲了避免這種情況,你必須從會議中刪除對象保存anythig事情之前:

try 
{ 
    token.Status = TokenStatus.Locked; 
    Session.SaveOrUpdate(token); 
} 
catch (StaleObjectStateException) 
{ 
    Session.Evict(token); // <- remove token from session 

    WriteLogsToDb(); // <- another StaleObjectStateException thrown here 
    return new FailedResult("Could not acquire token"); 
} 
+0

可悲的是,加入'Session.Evict'或'Session.Refresh'沒有效果。我忘記提到'Session.SaveOrUpdate'是一個覆蓋,它在內部創建一個事務,調用NHibernate的'SaveOrUpdate',然後提交事務。 – holdenmcgrohen

+0

請給我看看您正在調用WriteLogsToDb()的異常文本。和這種方法的代碼。 –

+0

在關於併發性的Ayende帖子中:http://ayende.com/blog/3946/nhibernate-mapping-concurrency 我發現這個: 如果更新因爲行更新而失敗,我們將得到一個StaleObjectException。與所有例外情況一樣,這會使會話不符合使用條件,您將不得不創建新會話來處理它._ 因此,您必須創建一個新會話才能在此情況下寫入日誌。 –