我对NHibernate的感受(3):有些尴尬的集合支持
既然是一個ORM框架,那么自然是將O這一端映射R上。至于集合,是O這方面最常見,也是R這一邊非常容易表示的關系。例如,一個問題(Question)可以包含多個回答(Answer),于是我的代碼里就有這樣的結構:
public class Question {public virtual int QuestionID { get; set; }public virtual string Name { get; set; }private ISet<Answer> m_answers;public ISet<Answer> Answers{get{if (this.m_answers == null)this.m_answers = new HashedSet<Answer>();return this.m_answers;}private set{this.m_answers = value;}} }public class Answer {public virtual int AnswerID { get; set; }public virtual string Name { get; set; }public virtual Question Question { get; set; } }于是這里就有個問題:為什么Answers屬性需要同時讀寫?有的朋友可能會說,NHibernate支持對私有變量的直接讀寫,這樣就可以對外暴露出只讀的屬性了。這個說法的確沒錯(而且我已經在這里使用private set了),不過這并不是我這里不滿意的地方。更準確的說,我的質疑是“為什么NHibernate會需要設置整個集合容器”?試想一下,在平時的開發中,我們的操作都是向一個集合中添加/刪除對象,而不會傻傻地修改對象的集合屬性。因為這個集合是對象自己維護的,而不是交給外界去“一鍋端”地設置。
可以設置的容器屬性并不僅僅是“感官”上的問題。假如,我使用了上面代碼,那么我在向數據庫插入數據時可能就是這樣做的:
var question = new Question(); question.Answers.Add(new Answer { Name = "Answer 1", Question = question }); question.Answers.Add(new Answer { Name = "Answer 2", Question = question });// put it into session看看這兩句紅色的代碼是不是有些多余?不僅僅是多余,這兒的問題在于,如果可以這樣自由設置Question屬性的話,那么我們是不是也有可能“一不小心”造成Answer與所在Question不匹配的問題呢?僅僅是創建還好,如果在一個場景下需要同時操作兩個Question或Answer,它們的關系可能就復雜了。NHibernate就是這樣,它需要我們手動地維護Question和Answer的雙向引用,否則插入/刪除/更新都可能不正確。
有些人的解決方法是添加額外的方法,例如AddAnswer:
public class Question {...public void AddAnswer(Answer answer){if (answer.Question != null){answer.Question.Answers.Remove(answer);}answer.Question = this;this.Answers.Add(answer);} }使用AddAnswer方法便可以自動地剝離Answer與原有Question的關系,并且與新的Question建立聯系了。同理,從一個Question對象中刪除一個Answer對象,或者修改Answer對象的Question屬性,應該都會引起雙方關系的變化。但是,即便我們提供了完整的關系維護手段,Question.Answers還是對外暴露,開發人員還是可以修改Answers集合。
因此,最好的辦法其實應該是在集合中提供一種維護關系的方式。例如LINQ to SQL在這一點上便做的不錯:
public partial class Question { private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);private int _QuestionID;private string _Name;private EntitySet<Answer> _Answers;public Question(){this._Answers = new EntitySet<Answer>(new Action<Answer>(this.attach_Answers),new Action<Answer>(this.detach_Answers));}public int QuestionID { ... }public string Name { ... }public EntitySet<Answer> Answers{get{return this._Answers;}set{this._Answers.Assign(value);}}private void attach_Answers(Answer entity){entity.Question = this;}private void detach_Answers(Answer entity){entity.Question = null;} }看看LINQ to SQL對我們多體貼,自動生成的代碼會幫我們維護Question與Answer之間的雙向關系。當然,還有一部分邏輯是在Answer類的Question屬性中,如果您感興趣可以自己去觀察一下。不過,LINQ to SQL的問題在于它使用了特殊的類型EntitySet,它會使用兩個回調函數對外公布集合內元素的添加/刪除情況。按理來說,如果我們想要在NHibernate中采用這種“自動維護”的方式,可以使用自定義的集合類型,例如:
private ISet<Answer> m_answers; public ISet<Answer> Answers {get{if (this.m_answers == null)this.m_answers = new CallbackSet<Answer>(...);return this.m_answers;}private set{this.m_answers = value;} }只可惜,在新建對象的時候我們自然利用到CallbackSet<Answer>,其中包含了我們定義的邏輯。但是如果是這樣的代碼呢?
var question = session.Get<Question>(1); question.Answers.Add(new Answer { Name = "Answer 1", Question = question }); question.Answers.Add(new Answer { Name = "Answer 2", Question = question }); session.Flush();在從數據庫中獲取Question對象的時候,NHibernate便會“自作主張”地將Answers屬性“整個”設為自己的ISet<Answer>對象——因為實現延遲加載,它也并不一定是HashedSet<Answer>。換句話說,NHibernate雖然能夠保持屬性的邏輯,但它不能保持自定義集合的邏輯。在我看來,NHibernate完全可以做到放棄集合屬性的set操作,把所有的對象都通過集合的Add方法添加進去。其實這樣做同樣可以實現集合的延遲加載,就好比放棄對所有方法的強制virtual要求,也能實現對象的延遲加載一樣。
為了避免像上次那樣誤解NHibernate,我剛才又作了一次測試——這次我應該沒有搞錯。當然,如果NHibernate支持對自定義集合類型那就再好不過了,我們就有辦法解決這個問題。但是我不知道該怎么做,如果您知道的話,請告訴我。在我看來,目前的問題是NHibernate對于POCO支持有缺陷造成的。如果是這樣的話,那我們的Model就不得不繼續遷就NHibernate了。
關于NHibernate集合還有一個有趣的問題是——請關注上面這4行代碼(Get-Add-Flush這段),這是一個非常標準也是非常常見的添加Answer對象的方式。只可惜,在調用ISet<Answer>的Add方法添加Answer對象的時候,會引發一次數據庫查詢操作,加載當前Question下的所有Answer——但是在我看來這根本沒有必要啊。我只是“添加”,并沒有要查詢。其實NHibernate幫我把新的Answer對象保存起來就可以了,為什么要增加無畏的開銷呢?當然我承認,這個做法會產生一些麻煩,例如需要將集合的操作分為“讀”和“寫”兩類,當“寫”操作發生時不會加載數據,而只有在第一次“讀”的時候才去數據庫查詢?!白x”和“寫”分離,本來就應該這樣。
那么誰又做到這一點了呢?又是LINQ to SQL。其實LINQ to SQL在細節上有非常多的考慮,使用起來也是非常容易的——如果我不是被它“寵壞”的話,可能也就不會在意NHiberante的這個問題了。
只可惜,對于ORM的生命“映射方式”上,LINQ to SQL的支持過于有限,這也大大限制了項目對它的接受程度。
?
轉自:http://blog.zhaojie.me/2009/10/my-view-of-nhibernate-3-collection-support.html
轉載于:https://www.cnblogs.com/zjoch/p/4360022.html
總結
以上是生活随笔為你收集整理的我对NHibernate的感受(3):有些尴尬的集合支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: meta http-equiv=X-UA
- 下一篇: SQL Server 2014如何提升非