Hello World
Spiga

我对NHibernate的感受(1):对延迟加载方式的误解

2009-09-24 14:04 by 老赵, 17505 visits

NHibernate是.NET平台上最著名的ORM框架,虽说出身于Java平台上的Hibernate,但是从外部看来这几乎就是一个.NET平台上的原生产品:有自己的社区,有自己的用户,有自己的商业支持,有利用C#特性的独立扩展。它不像Lucene.NET那样,一眼就能看出浓重的Java气息,Java的命名方式等等。我用NHibernate时间不长,而NHibernate的复杂程度也决定了我无法像了解LINQ to SQL那样容易。不过在使用了一段时间过后,还是对它有一定体会。有欣喜,有误解,也有抱怨。

这几篇文章里我不打算多谈NHiberante的优点,因为它的优势实在过于明显。如果不考虑Telerik ORM这样的商业框架(因为我没用过,完全不了解),.NET平台上开源和免费的ORM工具几乎没有NHibernate的对手:LINQ to SQL使用的确容易,上手非常快,某些功能也非常细致(稍后会谈到),但对于ORM工具的灵魂“Mapping能力”实在是不敢恭维。前一段时间我也简单了解了一下微软新出的Entity Framework,虽然也秉承了微软一贯的易用性(如强大的LINQ支持),在Mapping能力上也有切实的提高,但是在功能和一些细节控制上还远不如NHibernate。毕竟NHibernate是经历了多年发展,对于各种情况几乎都有应对措施。如延迟与否,是使用select还是join获取数据,是否在集合加载时附加条件。此外,NHibernate的Interceptor能力所带来的扩展性也是让我比较满意的,不过这点有机会再详细谈一下。

总之,目前NHibernate是我最满意的ORM框架。

那么现在进入正文内容。首先我想谈一下自己对NHibernate实现方式上的一个误解,这个误解让我对NHibernate一直有着错误的抱怨,我还在几篇文章里不断重复对NHibernate的错误指责,目前已经纠正,希望不会造成太大问题。

这个误解,是我一直认为NHibernate使用了一种简单的延迟加载方式。例如有这样一个对象:

public class Article
{
    public virtual int ArticleID { get; set; }
    public virtual string TagNames { get; set; }
}

在延迟加载的时候,我一直以为NHibernate只是通过Emit生成一个Article的子类,然后把属性覆盖成简单读写,例如:

public class Article$LazyProxy : Article
{
    private string m_tagNames;
    public override string TagNames
    {
        get
        {
            return this.m_tagNames;
        }
        set
        {
            this.m_tagNames = value;
        }
    }
}

这么做的问题自然是让TagNames属性原本的逻辑丢失了。如果对于失血的DTO模型,这自然没有关系,因为这些属性本身内部没有逻辑。但是,我习惯使用领域驱动设计(DDD)的方式来为产品建模,因此在这些属性中很可能拥有一些业务逻辑。例如改变对象的其他一些状态,同步至其他字段,或是触发事件等等。因此,丢失属性逻辑对我的影响是致命的,这意味着我必须“照顾”NHibernate的特性进行编程,而在进行建模时就开始考虑持久化逻辑是DDD实践中的一大问题——虽然软件开发不是理想化的,权衡是正常的,但如果NHiberante只能应付失血的DTO模型,那么它就对不起它的业界盛名了。

可惜的是,NHibernate没有在这里翻船——所以可能更应该说“值得庆幸”——它使用了一种维持原有业务逻辑的延迟代理写法:

public class ArticleLazyProxy : Article
{
    public override string TagNames
    {
        get
        {
            var tagNames = ... // 加载数据
            base.TagNames = tagNames;
            return base.TagNames;
        }
        set
        {
            base.TagNames = value;
        }
    }
}

当然,这是我从“测试效果”中反推出来的情况,NHiberante的实际做法应该不会那么简单。如果您关注我的文章,会发现这就是我之前提出的最为理想的延迟代理实现方式,也是我在Eazy类库中使用的做法。我在实现了Eazy的基本功能之后,还因为它满足了我的要求而微微沾沾自喜了一把,谁知这一切早已被NHibernate拿下。我昨天晚上试验出这个结果之后也震惊了一把,不是因为NHibernate的强大(因为它本不该犯此低级错误),而是因为我不理解自己之前为什么会轻易地臆断NHibernate的做法?想象我还在多篇文章中抱怨过这点,昨天试验过后,我立即把自己能想到的无稽之谈都修改了。惭愧啊。

哎,莫装B,装B被雷劈

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

39 条回复

  1. 李永京
    *.*.*.*
    链接

    李永京 2009-09-24 14:09:00

    关注太多细节了......ArticleLazyProxy从哪里得到的?

  2. Ryan Gene
    *.*.*.*
    链接

    Ryan Gene 2009-09-24 14:09:00

    呵呵,nhibernate经过那么多年,的确不错,我第一次听说延时加载也是在看hibernate(java)的时候,那时候还觉得这个挺神秘。。。

  3. 文超
    *.*.*.*
    链接

    文超 2009-09-24 14:10:00

    还没用过 NHibernate,就开发速度而言,比之 LINQ TO SQL 谁快些?

  4. sheehan
    *.*.*.*
    链接

    sheehan 2009-09-24 14:11:00

    SF?很喜欢老赵的文章 虽然大多看不懂 囧

  5. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-24 14:17:00

    夸的又有些过了。。。。

  6. 老赵
    admin
    链接

    老赵 2009-09-24 14:21:00

    文超:还没用过 NHibernate,就开发速度而言,比之 LINQ TO SQL 谁快些?


    对于小项目来说,那应该还是LINQ to SQL比较快。

  7. 老赵
    admin
    链接

    老赵 2009-09-24 14:21:00

    Ivony...:夸的又有些过了。。。。


    还好吧,如果在一个正式点的项目里做,NH的确是不二之选。

  8. 老赵
    admin
    链接

    老赵 2009-09-24 14:22:00

    李永京:关注太多细节了......ArticleLazyProxy从哪里得到的?


    你又没有仔细看,说了是推测的了,呵呵。

  9. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-24 14:23:00

    这样本来就不应该啊: Article的属性彻底被丢弃了!
    即使我写父子类的话, 同一属性也不会这么去覆盖他, 除非你真的要。
    一直也没明白你说的问题居然是这个啊。
    public class Article$LazyProxy : Article
    {
    private string m_tagNames;
    public override string TagNames
    {
    get
    {
    return this.m_tagNames;
    }
    set
    {
    this.m_tagNames = value;
    }
    }
    }

  10. 老赵
    admin
    链接

    老赵 2009-09-24 14:26:00

    @craboYang
    所以我对NH犯这样的低级错误表示奇怪,现在看来果然是我错了,嘿嘿。
    // 在评论里贴代码还是用插入代码功能吧。

  11. 四有青年
    *.*.*.*
    链接

    四有青年 2009-09-24 14:27:00

    哎,莫装B,装B被雷劈。

    说的好,哈哈

  12. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-24 14:29:00

    sorry,习惯了,粘贴的时候显示的好,就习惯地以为他展现的好。

    居然我一直没回过神你的原意。 看来我已经是万卷丛中过,片页不沾身啊。 下次得认真多一点。

  13. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-09-24 14:30:00

    在那能找到nh的源码啊,好好研究下

  14. LeoXing
    *.*.*.*
    链接

    LeoXing 2009-09-24 14:31:00

    不过实际上NHibernate的延迟加载的实现是如何的呢?

  15. LeoXing
    *.*.*.*
    链接

    LeoXing 2009-09-24 14:32:00

    @温景良(Jason)
    NHibernate本身就是开源项目,直接就可以下载源码。

  16. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-24 14:35:00

    @Jeffrey Zhao
    借问一下,JIRA你有用没? 现在需要一些Extension,看你有没有。



    @Jason
    nhforge.org才是NH的官网。
    http://nhforge.org/blogs/nhibernate/
    源码在http://sourceforge.net/projects/nhibernate/files/

  17. 老赵
    admin
    链接

    老赵 2009-09-24 14:35:00

    @LeoXing
    就是生成一个代理类啊。虽然我给的代码是“推测”的,但也是有根据的合理推测,可以体现NH中延迟加载的原理。

  18. 小城故事
    *.*.*.*
    链接

    小城故事 2009-09-24 14:46:00

    不管怎么说,NHibernate确实是.Net中ORM框架的佼佼者。但Entity Framework是下一代的ADO.Net, Linq To Entity才是未来的主流。

  19. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-24 14:49:00

    话说也很久没看过NHibernate了。.NET与Java不仅仅是技术框架的不同,也是开发哲学的不同,就以我以前对NHibernate的了解,这并不是一个.NET开发哲学的东西。

  20. 老赵
    admin
    链接

    老赵 2009-09-24 14:51:00

    @Ivony...
    能不能具体说说?两者有很多东西是通用的呀。

  21. 老赵
    admin
    链接

    老赵 2009-09-24 14:52:00

    @craboYang
    没用JIRA……

  22. LeoXing
    *.*.*.*
    链接

    LeoXing 2009-09-24 15:02:00

    @Ivony...
    开发哲学。。。多介绍介绍,让我们见识见识~~~

  23. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-09-24 15:10:00

    这态度真好 我喜欢

  24. rocklau
    *.*.*.*
    链接

    rocklau 2009-09-24 15:13:00

    不容易,老赵总算遇到让自己囧一把的事了.

  25. YJJ
    *.*.*.*
    链接

    YJJ 2009-09-24 15:13:00

    支持老赵,打击装B

  26. 老赵
    admin
    链接

    老赵 2009-09-24 15:16:00

    @韦恩卑鄙
    态度是小事,关键是我之前散布谣言了,如果我自己憋着闷着,说不定就不会公开承认错误了,嘿嘿。

  27. 温景良(Jason)
    *.*.*.*
    链接

    温景良(Jason) 2009-09-24 15:37:00

    craboYang:
    @Jeffrey Zhao
    借问一下,JIRA你有用没? 现在需要一些Extension,看你有没有。



    @Jason
    nhforge.org才是NH的官网。
    http://nhforge.org/blogs/nhibernate/
    源码在http://sourceforge.net/projects/nhibernate/files/


    看起来怎么都不想nh的源码啊

  28. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-09-24 15:39:00

    Jeffrey Zhao:
    @韦恩卑鄙
    态度是小事,关键是我之前散布谣言了,如果我自己憋着闷着,说不定就不会公开承认错误了,嘿嘿。


    我说的是对自己说错话的态度 对于造成影响的责任的承担
    不是语气啥的小细节

    css8不就是胡搅蛮缠拖别人下水么 和陈水扁相似



  29. Otis's Technology Spac…
    *.*.*.*
    链接

    Otis's Technology Space 2009-09-24 15:57:00

    好像是一系列.. 期待呀...

    另外: 標題錯誤: NHiberante 應該是: NHibernate

  30. LeoXing
    *.*.*.*
    链接

    LeoXing 2009-09-24 16:21:00

    @Otis's Technology Space
    好细哦~~~还真的写错了~~~

  31. craboYang
    *.*.*.*
    链接

    craboYang 2009-09-24 17:46:00

    [Quote]
    @Jason
    看起来怎么都不想nh的源码啊 [/Quote]

    NHibernate.Linq-1.0.0.GA-src.zip

    src = source = 源代码。

    不是源码是什么呢?已经给您这么详细的信息了, 一定要主动去试啊。 我也就是顺手, 谁知道你不但google不用,连下载链接都不看,就想直接在你硬盘上啊! 够晕的。

  32. 垃圾呆
    *.*.*.*
    链接

    垃圾呆 2009-09-24 22:57:00

    最近在WCF项目中用Entity感觉那是在玩心跳啊~
    不测不知道一测吓一跳
    一个Entity对象load点东西再传序列化出来63KB~一个对象而已,而且Load的只是几个外键引用的对象,当通过简化这些对象(通过自定义一些简化对象,出去掉那些reference之类的东西,之后再序列化也就10KB
    感觉ORM会不会在这些应用中受限?

  33. JimLiu
    *.*.*.*
    链接

    JimLiu 2009-09-25 09:37:00

    get
    {
    var tagNames = ... // 加载数据
    base.TagNames = tagNames;
    return base.TagNames;
    }
    这段代码的确是很漂亮。

    Linq 2 SQL,我用它做了一些小玩意,但是我不是很满意,第一是对于复杂或者怪异的Mapping支持不够好,第二也没有像NH那样能很好地控制SQL以及结果的“形状”(ResultTransformer)。但是作为一个[轻量级]的ORM,我觉得它已经能在他的定位上胜任了。
    Entity Framework。。我没怎么用,但是当我第一次用,它就把我郁闷到了,居然Linq 2 SQL都支持的自定义枚举类型它都不能用,我实在是不理解啊不理解。有点奇怪的延迟加载方式也让我一时间摸不着头脑。
    NHibernate,这个我用的比较多,我用它写了个论坛。一个很大的优点——复杂——也是他一个重大的缺点。如果认真去研究它,才会发现它真的很强大。它几乎可以Mapping所有遇得到的情况。延迟加载的方式让人还是比较省心的,FetchMode的存在对控制SQL也很友好。Interceptor也是一个非常棒的设计。

    我不是旨在对比三者,只是想说明NHibernate是让我比较满意的一个ORM。

  34. 老赵
    admin
    链接

    老赵 2009-09-25 13:07:00

    @JimLiu
    对比就对比咯,你的看法基本和我相同。

  35. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-09-26 22:56:00

    个人是比较反对将LINQ 2 SQL当成ORM框架来看待的。

    很显然的,LINQ是为了在面向对象程序设计语言中直接操作关系型数据,而LINQ 2 SQL则是将整个DBMS利用LINQ技术完整的映射到面向对象的程序设计语言中。

    所以存储过程被映射成方法,查询表达式被映射成LINQ Expression,而表(关系型数据)在我的文章中早已说过是映射成强类型集合,即IEnumerable<T>,数据行(有序n元组)映射成对象(只有属性)。

    换言之,LINQ 2 SQL是一个真正意义上的ROM(关系-对象 映射)框架。

    只不过大家在使用的时候,发现这个ROM既费事儿又难用。不过我却一直认为LINQ 2 SQL的伟大并不会因为这个项目在应用上的失败而黯然失色。因为这是历史上第一次关系和对象完整的映射,尽管与NHibernate的理想,对象-关系映射相去甚远。。。。。

  36. 张荣华
    *.*.*.*
    链接

    张荣华 2009-09-29 09:39:00

    我还在几篇文章里不断重复对NHibernate的错误职责,目前已经纠正,希望不会造成太大问题。

    职责?是不是应该是指责啊?
    PS:个人感觉NHibernate的配置有些复杂的说, 小项目还是linq to sql好些。

  37. dsjian[未注册用户]
    *.*.*.*
    链接

    dsjian[未注册用户] 2009-10-03 22:40:00

    没用过NHibernate,个人感觉还用不上,要解决写SQL语句以及开发效率问题,还不如用linq to sql 或 一些其他小型ORM框架,如MySoft.Data.

  38. 老赵
    admin
    链接

    老赵 2009-10-03 23:56:00

    @dsjian
    的确,NH的优势是长期的复杂的,如果是小型项目,LINQ to SQL很不错。

  39. 13yan
    60.179.105.*
    链接

    13yan 2013-05-17 14:21:58

    没想到多年前园子里就已经有前辈对延迟加载的源码猜想。我发晚了。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我