2013-03-20 12 views
1

好吧,我有點難住這個NHibernate查詢。這個困惑在PasswordResetToken的周圍。NHIbernate「參考」屬性生成即使正確選擇+ 1外部連接

首先,這裏是映射:

public ContactMap() 
    { 
     Table("Contact"); 
     Id(x => x.ContactId, "ContactId").Unique().GeneratedBy.Increment(); 
     Map(x => x.EmailAddress); 
     ... 
     Map(x => x.JobTitle); 

     References(x => x.PasswordResetToken, "EmailAddress") 
      .PropertyRef(x => x.EmailAddress) 
      .Cascade.None() 
      .Not.LazyLoad() 
      .Not.Update(); 


     HasMany(x => x.Roles) 
      .Table("tblContactRole").KeyColumn("ContactId").Element("Role", part => part.Type<global::NHibernate.Type.EnumStringType<ContactRoles>>()) 
      .AsSet() 
      .Not.LazyLoad(); 
    } 

現在,這裏是查詢:

public IList<Contact> GetContacts(int id) 
    { 
     var contacts = Session.CreateCriteria<Contact>() 
          .Add(Restrictions.Eq("Id", id)) 
          .Add(Restrictions.Eq("IsActive", true)) 
          .SetFetchMode("Roles", FetchMode.Eager) 
          .SetFetchMode("PasswordResetToken", FetchMode.Eager) 
          .SetResultTransformer(CriteriaSpecification.DistinctRootEntity) 
          .List<Contact>(); 

     return contacts; 
    } 

我的理解是,FetchMode.Eager意味着JOIN使用子查詢代替,所以有沒有任何理由有額外的調用數據庫出現。

運行正確的SQL查詢返回保存聯繫人所需的所有信息,如NHProf(突出顯示的查詢)的屏幕截圖所示(不要擔心不同的表名等 - 我已對上面的代碼進行了清理):

NHProf

我不明白的是爲什麼在地球上幾十個不同的選擇將PasswordResetToken表的生成和運行?其中一個查詢僅針對沒有PasswordResetToken的每個聯繫人生成(即第一個查詢返回這些列的空值) - 不確定這與它有什麼關係。

聯繫人可能有或可能沒有幾個角色(這個問題是多餘的),並且類似地,可能有也可能沒有恰好一個PasswordResetToken。

數據庫是有點狡猾與少數外鍵。在這種情況下,Contact和PasswordResetToken之間的鏈接是一個簡單的共享列「EmailAddress」。

所有這些查詢都是在上面單行代碼的運行過程中生成的(即代碼不在循環中)。

讓我知道我是否缺少任何信息。

我應該使用Google搜索嗎?

回答

2

這是一個bug。我試圖讓它只用兩個查詢來工作,但是從bug報告來看,這聽起來像是一個挑戰。

附加測試顯示引用一個唯一屬性(而不是Id)的多對一關聯會導致選擇n + 1問題。儘管第一條語句包含正確的連接,但是在加入選擇後,所有關聯的實體都會被逐個提取。 (在唯一列中具有相同值的實體甚至被多次提取)

有趣的一點是,只有在被引用的 實體已在會話緩存中時纔會發生此錯誤。如果不是,則不會創建其他選擇語句 。

+0

我今天也是自己打的!幽靈,我剛剛投票錯誤越多,我們得到更多的機會,可能會得到修復。 – Rippo 2013-03-20 16:41:08

+0

謝謝傑米 - 從來沒有猜到它是一個錯誤。 – 2013-03-21 09:52:43