2009-09-25 72 views
0

我在NHibernate中遇到了一些獨特約束的麻煩。NHibernate的獨特約束條件

我有一個用戶實體映射到一個唯一的用戶名屬性約束。我想要做的是能夠在新用戶添加之前以及在現有用戶更新用戶名之前檢查特定用戶名是否存在。

第一種情況(添加一個新用戶)工作得很好。但是,當我嘗試在更新現有用戶之前檢查用戶名是否存在時,我得到了違反約束的情況。以下是我的Save方法的代碼。

public void Save<T>(T entity) where T : User 
    { 
     using (var session = GetSession()) 
     using (var transaction = session.BeginTransaction()) 
     { 
      try 
      { 
       CheckIfUsernameExists(entity); 

       session.SaveOrUpdate(entity); 
       session.Flush(); 
       transaction.Commit(); 
      } 
      catch (HibernateException) 
      { 
       transaction.Rollback(); 
       throw; 
      } 
     } 
    } 

的約束被違反在CheckIfUsernameExists()方法,它看起來像這樣:

public void CheckIfUsernameExists<T>(T entity) where T : User 
    { 
     var user = GetUserByUsername(entity); 
     if (user != null) 
      throw new UsernameExistsException(); 
    } 

    private T GetUserByUsername<T>(T entity) where T : User 
    { 
     var username = entity.Username; 
     var idToExclude = entity.Id; 

     var session = GetSession(); 

     var user = session.CreateCriteria<T>() 
      .Add(Restrictions.Eq("Username", username)) 
      .Add(Restrictions.Not(Restrictions.IdEq(idToExclude))) 
      .UniqueResult() as T; 

     return user; 
    } 

它是導致它崩潰從而導致NHibernateException的session.CreateCriteria()行(SQLiteException ),消息「由於違反約束而中止。列用戶名不唯一」。

它與NHibernate的現金有關嗎?傳遞給save方法的實體在session.CreateCriteria()被調用時已用所需的用戶名更新。也許我做這一切都錯了(我是一個NHibernate的初學者),所以請隨時陳述明顯並提出替代方案。

任何幫助非常感謝!

回答

0

嗯,我不確定問題的核心,但對於嘗試查看用戶是否已經存在的策略,爲什麼需要「.UniqueResult()」?

難道你不能只是假設得到一個與用戶名相匹配的用戶列表,並且沒有與你當前用戶相同的用戶名(顯然)。僞代碼,好像我做這樣的事情

public bool ExistsUsername(string username, int idToExclude) 
{ 
    IList<User> usersFound = someNHibernateCriteria excluding entries that have id = idToExclude 

    return (usersFound.Count > 0) 
} 
+0

回答你的問題是,我不知道。我已將代碼更改爲您的建議,但結果相同。感謝您的輸入! – 2009-09-25 10:30:46

+1

好的...我想你在你的數據庫中的「用戶名」col放置了一個唯一的約束?此外,我讀到,你有「傳遞給保存方法的實體已更新所需的用戶名在會議..」我不會這樣做。我會在之前調用ExistsUsername,這樣你也可以避免使用「idToAvoid」。 – Juri 2009-09-25 10:37:49

+0

是的,數據庫中有一個用戶名限制。關於在更新實體之前調用ExistsUsername;這可能是一種可能性,但我需要進一步調查。謝謝。 – 2009-09-25 10:59:27

0

兩個想法: - 你爲什麼不只是SaveOrUpdate,看看你是否成功。這在你的情況下是不可能的? - 我見過你提到SQLite。這是你真正的生產系統,還是你用來測試的東西?如果是這樣,你是否檢查過SQLite是否會造成問題,而查詢是否可以針對全功能的DBMS? - SQLite經常遇到這樣的問題,因爲它不支持所有類型的約束......

+0

托馬斯,我希望能夠調用SaveOrUpdate,但由於我無法檢查拋出什麼類型的異常,我沒有辦法通知用戶出了什麼問題。這就是爲什麼我想拋出自己的異常(UsernameExistsException)。或者有沒有辦法讓我不知道的更具體的NHibernate異常? 關於SQLite;是的,我們在本地開發mashines上使用SQLite,並進行集成測試。我們目前沒有建立生產環境。我會牢記它,但SQLite似乎不可能處理這種簡單的情況。 – 2009-09-25 10:54:58

+0

您是否通過NH映射通過hbm2ddl生成SQLite實例?那麼這就是問題所在:SQLite不支持'ALTER TABLE'語句,AFAIK它們被默默地忽略。 如果您計劃在生產中定位另一個DBMS,那麼您就不能使用SQLite進行集成測試 - 這不是完全替代。這可能是快速和容易的,但它可能會給出錯誤的結果。 你應該花半個小時對一個完整的DBMS測試這段代碼 - 我確信測試會通過... – 2009-09-26 04:39:53

+0

@Thomas Weller:恐怕情況並非如此。我們刪除表中的表並使用模式導出來添加新表。關於SQLite進行集成測試,您的聲明與我從很多人聽到的完全相反。爲什麼不能用它進行集成測試?感謝您的輸入! – 2009-10-08 20:46:11

0

你確定在CreateCriteria引發異常嗎?因爲,我沒有看到如何從select語句獲取SQLlite約束異常。我做同樣的事情......

public bool NameAlreadyExists(string name, int? exclude_id) 
{ 
    ICriteria crit = session.CreateCriteria<User>() 
     .SetProjection(Projections.Constant(1)) 
     .Add(Restrictions.Eq(Projections.Property("name"), name)); 

    if (exclude_id.HasValue) 
     crit.Add(Restrictions.Not(Restrictions.IdEq(exclude_id.Value))); 

    return crit.List().Count > 0; 
} 

我會看看生成的SQL的順序,看看是什麼原因造成的。如果該實體在該會話中加載,則可能在查詢之前更新該實體。