2012-02-22 78 views
2

我想處理情況,即用戶從一個數據庫中的Windows窗體應用程序編輯對象的情況下,使得這將違反數據庫約束(即列的唯一值)的編輯,保存實體返回到數據庫,NHibernate拋出一個異常,從而破壞會話。約束違規處理中的WinForms使用NHibernate

我使用發佈MSDN文章Building a Desktop To-Do Application with NHibernate的指導和使用的session-per-主持人/形式的做法。在創建演示者時,我保留對SessionFactory的引用,創建一個新會話,通過會話從數據庫檢索對象並存儲對該對象的引用。當顯示錶單時,其字段將從對象中填充。

更改時的形式製成,並且用戶希望保存的數據,該對象從字段值更新,和所述對象被保存到數據庫中。

我處理陳舊狀態的異常,其中對象在從數據庫中檢索和保存回來之間發生變化:創建新會話,再次裝入原始對象,通知用戶衝突已經發生並且與他的變化以及數據庫中當前的內容一起呈現。他可以選擇保存更改或取消(並接受現在存儲在數據庫中的內容)。

在違反約束的情況下,它似乎是兩件事情之一可能發生:

  1. 對象在它可以被重新加載到新的會話的數據庫並沒有改變。
  2. 該對象在數據庫中也進行了更改,不再反映最初加載的內容。

但是,我不認爲我實際上可以檢測到發生了哪種情況,因爲發生約束異常(我測試過這種情況)從來沒有拋出過時狀態異常。

辦案1是微不足道的,因爲我可以簡單地顯示,上面寫着「FIELD-X具有的值已經在DB」,假裝沒有什麼糟糕的事情發生的錯誤消息。用戶可以將FIELD-X更改爲唯一值並再次保存,而無需重新輸入其更改。

辦案2會像處理一個普通陳舊的狀態異常。

我知道我可以「暴力」這個由保持原件及複印件(或它的值),然後將兩個場逐場比較。 但是,我懷疑有一個更好的方法來利用NHibernate來處理這個問題。你會如何處理?

如果它是有幫助的:

  • NHibernate的版本:3.2
  • 樂觀鎖使用 「髒」 的戰略
  • 的.NET Framework 2.0 SP2
  • SQLite數據庫/驅動器

編輯2月23日 - 我添加了一些示例代碼,以更好地說明我目前正在做什麼。

/** 
* Save handler called when user initiates save in UI. 
* 
* Returns true when save was successful (essentially, tells the presenter 
* that the UI can be closed. 
*/ 
private bool SaveData() 
{ 
    try 
    { 
     if (this.creatingNewUserAccount) 
     { 
      // Do whatever is necessary to instantiate a new object. 
      this.userAccount = new UserAccount(); 
      // and copy values from the UI into the new object. 
      this.userAccount.Name = this.form.Name; 
      // etc. 
     } 
     else 
     { 
      // Just copy values from the UI into the existing object 
      // from the database. 
      this.userAccount.Name = this.form.Name; 
      // etc. 
     } 

     using (ITransaction tx = this.session.BeginTransaction()) 
     { 
      this.accountRepository.store(this.userAccount); 
      tx.Commit(); 
     } 

     return true; 
    } 
    catch (StaleObjectStateException) 
    { 
     HandleStaleStateException(); 
     return false; 
    } 
    catch (ArgumentException e) 
    { 
     this.m_View.ShowOtherDialog(e.Message); 
     return false; 
    } 
    catch (GenericADOException e) 
    { 
     HandleConstraintViolationException(); 
     return false; 
    } 
} 

private void HandleStaleStateException() 
{ 
    // The session was trashed when the exception was thrown, 
    // so close it and create a new one. 
    this.session.Dispose(); 
    this.session = this.sessionFactory.OpenSession(); 
    CurrentSessionContext.Bind(this.session); 

    // Reload the object from the database. 
    this.userAccount = LoadData(); 

    // Do a bunch of things that deal with informing the user 
    // of the stale-state and displaying a form to merge changes. 
    HandleEditConflict(); 
} 

private void HandleConstraintViolationException() 
{ 
    // The session was trashed when the exception was thrown, 
    // so close it and create a new one. 
    this.session.Dispose(); 
    this.session = this.sessionFactory.OpenSession(); 
    CurrentSessionContext.Bind(this.session); 

    // Determine if trying to save a new entity or editing an existing one. 
    if (this.creatingNewUserAccount) 
    { 
     // If saving a new entity, we don't care about the old object 
     // we created and tried to save. 
     this.userAccount = null; 
    } 
    else 
    { 
     // ???? 
    } 
} 

回答

1

ISession.Refresh(Object obj)方法是什麼結束了對我的工作不感興趣的內容。從我的問題的代碼仍然是除了最後一種方法是相同的:

private void HandleConstraintViolationException() 
{ 
    // The session was trashed when the exception was thrown, 
    // so close it and create a new one. 
    this.session.Dispose(); 
    this.session = this.sessionFactory.OpenSession(); 
    CurrentSessionContext.Bind(this.session); 

    // Determine if trying to save a new entity or editing an existing one. 
    if (this.creatingNewUserAccount) 
    { 
     // If saving a new entity, we don't care about the old object 
     // we created and tried to save. 
     this.userAccount = null; 
    } 
    else 
    { 
     this.session.Refresh(this.userAccount); 
    } 
    this.form.ShowDialog("... Describe the constraint violation ..."); 
} 
0

你可以

// after ConstraintException with new session 

session.Lock(loadedObject, LockMode.None); // attach object with session 
// copy back from UI 
session.Flush(); 
catch() 
{ 
    if (ConstraintException) 
     // repeat 
    else if (Stale) 
     // handle like you have 
    else 
     // all ok 
} 

,如果你什麼是分貝

// loads from the database, copy state from object into it and returns the loaded object (attached to session), changes will be updated on next flush 
obj = session.Merge(obj); 
+0

我不知道如果我把你的代碼放到我自己的意圖,你的方式 - 誠然,當你回答,我沒有任何代碼張貼給你看看 - 但這並沒有爲我工作。 Lock()似乎沒有重新連接實體,而Flush()只是試圖再次將其保存到數據庫。但是,使用Merge()的建議讓我更深入瞭解API並找到了Refresh()。 – 2012-03-05 21:49:59