2012-05-30 78 views
2

嘗試保存屬於多對多關聯的對象時,出現TransientObjectException異常。我有點理解爲什麼會發生這種情況,但想知道如何正確完成我正在嘗試做的事情。如何正確更新與Transient對象之間的NHibernate多對多關係? TransientObjectException

我所要做的,概括地說:

我的應用程序的用戶列表和角色的列表。用戶可以分配給多個角色,角色可以分配給多個用戶。有一個管理員可以執行這些分配的網頁,分配可以在兩個方向完成(例如,管理員可以選擇一個用戶,然後爲其添加角色;或者選擇一個角色並向其添加用戶)。

例如,假設管理員點擊用戶「Alice」上的「編輯」。管理員將看到可用角色列表以及已分配給Alice的角色列表。管理員然後爲Alice分配一個新角色並點擊「保存」。

在服務器上,從客戶端接收臨時用戶和分配的角色對象。如果我只將瞬態角色列表分配給用戶對象(例如,user.Roles = roles),我可以更新得很好。然而,如果我在做這個任務之前碰巧從數據庫中讀用戶,我會在關聯的Role對象上得到一個TransientObjectException。

類定義:

​​

映射:

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> 
    <class name="Role"> 
    <id name="ID"> 
     <generator class="guid"/> 
    </id> 

    <bag name="Users" table="Role_User" lazy="false" cascade="none"> 
     <key column="RoleID" /> 
     <many-to-many column="UserID" class="User" /> 
    </bag> 
    </class> 
</hibernate-mapping> 

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"> 
    <class name="User"> 
    <id name="ID"> 
     <generator class="guid"/> 
    </id> 

    <bag name="Roles" table="Role_User" lazy="false" cascade="none"> 
     <key column="UserID" /> 
     <many-to-many column="RoleID" class="Role" /> 
    </bag> 
    </class> 
</hibernate-mapping> 

代碼保存(作品)

public void UpdateUser(User user, IList<Role> associatedRoles) 
{ 
    using (var session = _sessionFactory.OpenSession()) 
    { 
     user.Roles = associatedRoles; 
     session.Merge<User>(user); 
    } 
} 

代碼保存(失敗)

public void UpdateUser(User user, IList<Role> associatedRoles) 
{ 
    using (var session = _sessionFactory.OpenSession()) 
    { 
     User originalUser = session.Get<User>(user.ID); 
     // Code that does some audit reporting/logging 
     LogDifferences(originalUser, user); 
     user.Roles = associatedRoles; 
     session.Merge<User>(user); 
    } 
} 

對象是一個未保存的瞬態的實例 - 合併

+1

我認爲問題在於你的會話將有兩個具有相同ID的用戶實例。嘗試使用session.Evict()從會話中刪除原始用戶。 –

回答

0

Miroslav Popovic的觀點非常相關;然而,在我的情況下,我遇到了問題,因爲數據庫中有一個版本設置爲空的GUID的實體。這些記錄是在NHibernate之外創建的。當保存它們之間的關聯時,NHibernate失敗了,因爲它檢測到其中一個關聯條目是由於它的0版本而成爲臨時對象,即使它引用了現有條目。

1

Jouni阿羅是對之前保存的瞬態的實例......你不能有實體的兩個實例與會話中使用相同的ID。

的問題是下列之一:

  1. 你正試圖從數據庫加載originalUser,但user已連接到您裝入的originalUser同一會話
  2. ,之後試圖合併user到同一會議 - 先驅用戶原創,因爲Jouni建議

無論如何,你真的需要加載原始用戶只是爲了找出這些變化是什麼?還有其他方法可以用NHibernate來做到這一點。你有沒有試過interceptors

這裏有一個潛在的用法:http://nhforge.org/wikis/howtonh/finding-dirty-properties-in-nhibernate.aspxGoogle有點多了。

+0

我認爲,這是我對NHibernate的主要煩惱之一 - 如果它可以檢測到存在與非瞬時實例衝突的瞬態實例,爲什麼不自動執行驅逐?我不確定攔截器是如何工作的,因爲這是從客戶端返回到服務器之後 - 服務器沒有進行某種比較(如上面的代碼)沒有單個更改的值的上下文。 – Travis

+0

實際的代碼並沒有直接處理會話(它被抽象出來),所以我可能會以不同但類似的方式解決這個問題。而不是從會話中逐出對象,我將在並行會話中加載它,執行diff,然後關閉並行會話。 – Travis

+0

我認爲你正在遇到案例#2。嘗試將用戶合併到已具有非瞬時originalUser的同一個會話。如果NHibernate做了一些自動驅逐,你的原始用戶將被踢出,用戶將被合併。它可能適用於你的情況,但它可能會導致一些微妙的,很難追蹤/調試其他人代碼中的錯誤。 –