既然是一个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对象保存起来就可以了,为什么要增加无畏的开销呢?当然我承认,这个做法会产生一些麻烦,例如需要将集合的操作分为“读”和“写”两类,当“写”操作发生时不会加载数据,而只有在第一次“读”的时候才去数据库查询。“读”和“写”分离,本来就应该这样。

那么谁又做到这一点了呢?又是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):有些尴尬的集合支持相关推荐

  1. 我对NHibernate的感受(2):何必到处都virtual

    上一篇主要是在夸NHibernate实现的好,而这篇就完全是来抱怨的了.NHiberante有个毛病,就是如果是和数据库产生映射的类,就要求所有的public成员和protected成员必须是virt ...

  2. 艾伟_转载:我对NHibernate的感受(1):对延迟加载方式的误解

    NHibernate是.NET平台上最著名的ORM框架,虽说出身于Java平台上的Hibernate,但是从外部看来这几乎就是一个.NET平台上的原生产品:有自己的社区,有自己的用户,有自己的商业支持 ...

  3. NHibernate自定义集合类型(上):基本实现方式

    前天一篇文章中我说NHibernate的集合类型实现有些"尴尬",它无法使用自定义集合类型,设计也有些古怪--不过在许多朋友的指点下,我意识到NHibernate是可以使用自定义集 ...

  4. PNAS:以移情尴尬为主的社交焦虑者大脑和行为改变

    <本文同步发布于"脑之说"微信公众号,欢迎搜索关注~~> 社交焦虑障碍包括害怕在别人面前让自己尴尬.金泰-共济失调(TKS),一种在东亚常见的亚型,此外还包括对使他人尴 ...

  5. ASP.NET MVC+Spring.net+Nhibernate+EasyUI+Jquery开发案例(1)

    前言:由于这段时间公司的事情比较的繁忙,每天都要学习新的东西,所以好久没有写博客了,今天抽空写写博客来记录我这段时间研究的东西,给自己加深一下印象,我用easyUI的组件开发了一个简单操作数据库的小样 ...

  6. [NHibernate]集合类(Collections)映射

    系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate ...

  7. bitwise oracle,linq – 与NHibernate和Oracle的Bitwise AND

    我正在使用Fluent NHibernate 1.0RC(对于NHibernate 2.1.4000)以及 Linq 2 NHibernate,我想用按位和操作执行查询.我第一次尝试使用这样的Linq ...

  8. 解决因蓝牙驱动版本问题无法升win10问题 (附 WIN7升级WIN10超详细步骤 及 升级后个别问题软件处理方法)

    昨天晚上刚升级完毕,现在已经在正常使用! 有什么问题的朋友,欢迎多多交流! 想看如何解决因为蓝牙驱动无法升win10问题的朋友,直接看 升级过程中解决问题 的章节.其实思路就是如果真的无法更新驱动,那 ...

  9. mili u盘 android手机,极客们的扩容共享神器 MiLi iData Air使用体验

    无论是使用iPhone还是使用Android手机,最令人烦恼的应该就是相册里满满当当的视频和照片了,每当系统提示空间不足的时候,一台扩容神器当然不能缺少. 我们市面上可见的扩容神器也不少,比如Andr ...

最新文章

  1. Linux下多线程编程互斥锁和条件变量的简单使用
  2. Dubbo 在maven项目中的应用
  3. 1.PostgreSQL的学习
  4. C语言程序练习- L1-040 最佳情侣身高差 (10分)
  5. linux系统下替换图片,Linux(ubuntu系统)下使用FreeImage库
  6. 推荐几款好用的模态框附带教程
  7. 13号线ab线规划图_南宁地铁线最新规划(20202035年)!喜添8条轨道普线、1条环线和4条快线…...
  8. python 基础简单猜数游戏
  9. python正则匹配中文_python 正则表达式匹配中文-阿里云开发者社区
  10. python自动化学习之语法学习(判断语句和循环语句)
  11. .net 5和.net core_开源仿真器 EpicSim 运行 SM3_core
  12. 湖北工业大学计算机导论考试试题,湖北工业大学计算机二级考试时间
  13. 【机器学习】K近邻(KNN)算法详解
  14. 微信公众号开发流程指南
  15. keyshot渲染图文教程_keyshot渲染图文教程,教你如何渲染冰的效果
  16. 程序员必知的编程5祖师爷,不要说你不知道!
  17. AIGC在营销图片生成技术综述
  18. 如何优化我的世界服务器,我的世界服务器优化教程优化插件和玩法建议
  19. 状态压缩dp学习小记part2
  20. 楼梯计算机公式,楼梯计算公式

热门文章

  1. Linux基础学习四:Linux常用的命令(非常详细)
  2. ThinkPHP6项目基操(12.实战部分 验证码)
  3. WEB安全基础-SQL注入基础
  4. java基本数据类型的标识符_java基础(一)-标识符、变量、基本数据类型及转换、运算符及表达式...
  5. c语言编程顺序查找例题,C语言典型编程例题.doc
  6. 基于云原生2.0,华为云沃土云创计划全面使能伙伴创新升级
  7. python语言的核心理念是_Python 编程语言的核心是什么?
  8. js一次获取整个表单的数据
  9. 测试学开发——第一课:环境搭建与页面开发介绍。
  10. Loadrunner 接口测试的两种方法