代码改变世界

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

2009-09-24 14:04 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏

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被雷劈

相关文章