2017-03-03 61 views
1

我目前正在修復舊的Windows應用程序並遇到NHibernate錯誤。我已經閱讀並在網上嘗試了一些東西,但最終出現錯誤。NHibernate SaveOrUpdate錯誤

這裏是我的ISession的代碼:

Public ReadOnly Property session() As ISession 
    Get 
     If IsNothing(m_session) Then 
      m_session = Factory.InitConfiguration.OpenSession() 
     End If 
     Return m_session 
    End Get 
End Property 

這裏是我的保存按鈕代碼:

 Try 
      session.BeginTransaction() 
      SetParent(x_object) 
      'session.clear() 
      session.Flush() 
      session.SaveOrUpdate(x_object) 
      session.Transaction.Commit() 
      compObj.IsNew = False 
      Return True 
    Catch ex As Exception 
     AppServices.ErrorMessage = ex.Message 
     session.Transaction.Rollback() 
     Return False 
    Finally 
     'TBA 
    End Try 

所以,問題就在這裏,我有這個日期列作爲DateTime和AttachmentList 。

當前的代碼在年份中的用戶鍵值小於1753之前沒有任何問題。但是,代碼正確捕獲錯誤並顯示消息,並且當用戶繼續修復年份錯誤時,它會仍然捕獲錯誤(在手錶中我已經獲得新值),直到用戶關閉應用程序並重新打開它。

但是,如果我取消註釋session.clear(),它會做得很好,用戶可以修復他們的拼寫錯誤並繼續保存記錄,但是當用戶執行其他操作時可以說附件,它會得到另一個錯誤。附件動作如下:

  1. 添加附件
  2. 點擊保存按鈕
  3. 添加新的附件
  4. 點擊保存按鈕
  5. 新的錯誤。

enter image description here

所以請指點一下什麼需要做的工作。我試過合併,我試過更新,保存,驅逐,但最終出錯。我認爲我的問題是我如何安排會議是問題的主要來源。

+0

「m_session」和「x_object」的範圍是什麼?你是否將實體映射到DTO之類的其他實體? –

+0

@A_J是的,我將實體映射到許多孩子,並將其設置爲1父母。然而,我的問題是通過弗雷德裏克的解釋解決的。你可以參考爲什麼。 –

回答

0

它看起來像你必須處理嘗試去使用一個會話在經歷了一個失敗的沖洗或事務提交的代碼。

這是一種反模式。從NHibernate reference

NHibernate的使用可能會導致異常,通常是HibernateException。 此異常可能具有嵌套的內部異常(根本原因),請使用InnerException屬性來訪問它。

如果ISession引發異常,應立即回滾 事務,調用ISession.Close()並放棄ISession 實例。 ISession的某些方法不會使會話保持一致的狀態。

...

下異常處理成語顯示了NHibernate的應用典型案例:

using (ISession sess = factory.OpenSession()) 
using (ITransaction tx = sess.BeginTransaction()) 
{ 
    // do some work 
    ... 
    tx.Commit(); 
} 

或者,當手動管理ADO.NET事務:

ISession sess = factory.openSession(); 
try 
{ 
    // do some work 
    ... 
    sess.Flush(); 
    currentTransaction.Commit(); 
} 
catch (Exception e) 
{ 
    currentTransaction.Rollback(); 
    throw; 
} 
finally 
{ 
    sess.Close(); 
} 

您必須確保代碼在異常發生後不會嘗試繼續使用會話。

此外,它看起來像會停留在等待用戶交互打開:這不是一個推薦的模式。 NHibernate會話通常是短暫的。開幕式很便宜。

的通常模式是開始處理來自用戶輸入的事件和終止事件處理之前關閉它,爲了不留下當用戶走了走咖啡開業時將其打開。

現在你可能很難有時間更改應用程序會話管理,尤其是在應用程序保留引用實體和期望它們仍然被綁定到一個打開的會話已經等待用戶交互之後。

如果保持用戶之間的互動會議開幕的選擇是爲「利用一級緩存」(會話實體緩存)來完成,可以考慮啓用二級緩存代替。

+0

現在我終於從你的解釋中明白了,並解決了我的問題,你的解釋確實幫助我找到了一個解決方案,但是最終我的代碼邏輯在對象上使用了SaveOrUpdateCopy。現在是時候將這種老方法改爲新方法了。感謝兄弟的解釋! –

0

您還沒有提及您的m_sessionx_object對象的範圍。另外,你還沒有提到你如何處理實體。你是否將實體映射到DTO之類的其他實體?

讓我們看看爲什麼出現這種情況:

這可能會發生由於第一級(會話)的緩存。您嘗試SaveOrUpdate一個實體;它由於數據不正確而失敗。但是,實體仍處於會話緩存中。然後用戶糾正錯誤並再次保存。您再次使用SaveOrUpdateNEW與已在會話中的實體發生衝突的實體。兩個實體的標識符是相同的。

請參閱thisthis的問題。

當您取消註釋session.clear()時,第一個實體離開會話,您的新實體正常工作,因爲現在沒有衝突。

我上面提到的鏈接也建議如何處理這種情況,而不是clear()

Clear對我的理解並不好解決;它會清除整個會話。 Evict是更好的選擇。如果Evict不適合你,可能是你沒有驅逐正確的實例。

以下是C#代碼;你需要把它翻譯成VB.NET: -

try 
{ 
    nhSession.SaveOrUpdate(instance); 
} 
catch(NonUniqueObjectException) 
{ 
    T instanceFromCache = GetInstanceFromCache<T>(instance); 
    nhSession.Evict(instanceFromCache); 
    nhSession.SaveOrUpdate(instance); 
} 

您可以從我上面提到的鏈接之一得到GetInstanceFromCache方法。

+1

你的方法對我有用,但是它有點慢,加上其他選項卡遇到的新問題。然而,我的問題是從使用Saveorupdatecopy時@Frederic post的回答中解決的。非常感謝您的解釋,我現在瞭解NHibernate,因爲我新探索這些東西。 –