如果你曾经用过NHibernate 2.0或者更高的版本,那您一定碰到过下面的错误:NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
NHibernateExamples.Entities.OrderLine: method get_UnitPrice should be 'public/protected virtual' or 'protected internal virtual'
NHibernateExamples.Entities.OrderLine: method set_UnitPrice should be 'public/protected virtual' or 'protected internal virtual'

哎呀,我们忘记把OrderLine实体中的UnitPrice属性标志成virtual的了,奇怪的是,为什么它一开始的时候必须要设置成virtual的,这是一个对于初次接触NHibernate的人经常有的疑惑。

针对这个问题,最简洁的答案就是:因为我们需要把成员设置成virtual的,是为了实现我们的延迟加载的魔幻功能。

但是更详细点的答案反而更有趣,我们知道任何真正的ORM必须要有的一个重要功能就是延迟加载,如果你通过ORM获取一个对象,你不会希望它去自动的获取整个对象图中所有数据(默认情况下应当不是), 还有你也不希望添加一些凌乱的代码去检查特定的关联关系是否已经被加载了,我们需要的是只有我们的需要的时候才去加载它们。这应该是ORM的职责。理想情况下应该是,你能访问这些属性,而且如果这时数据还没有加载,当你第一次访问的时候,ORM会负责加载当前需要的数据。

NHiernate 拥有这项能力,但是它却不要求你继承NHibernate中的某项基类或者实现一些接口。那奇怪了? Nhibernate是怎么做到的呢?其实,当需要延迟加载时,在运行期,Nhibernate用的是你的类的代理(proxy)。 那代理又是什么呢? 一个Nhibernate代理指的是当你的程序初始化Nhibernate的时候(发生在应用程序第一次启动的时候,并且只发生一次),Nhibernate动态生成的一个类型。如果你没有显示的给你的Entity指定不需要延迟加载,那Nhibernate会为你的每一个Entity生成一个代理,每一个Entity代理的类型其实是继承与这个Entity的。这样你在操作这个类型的时候,你就可以做一些拦截操作。

为了使这个过程变得更清晰,我们举个小例子。假设你有一个Order类,这个Order类有一些属性如Employee,Customer和其它。 但是当你加载Order实体的时候,你也许不想让Employee属性已经包含了真正的Employee实例。对于Cutomer属性也是同样的。默认情况下,Nhibernate认为每个实体类型都是要延迟加载的,除非你显示的告诉它不用延迟加载。所以,当Nhibernate一开始初始化的时候,它会知道它需要动态的为Customer和Employee生成代理(proxy)类型。在这我们假设两个类型的名字为CustomerProxyType和EmployeeProxyType (在现实中它们不会叫这个名字,但是这个不重要). 现在假设你去获取Order实例,这时你还没有去请求Customer或者Employee的数据,所以它们应当是不存在的,对吧? 但是它们也不为Null, 因为Nhibernate 为Customer属性分配了CustomerProxyType的实例,为Employee属性分配了EmployeeProxyType实例。并且还为每一个实例初始化了它们的标识符的值,一般是ID的值。这个标识符的值是在查询order记录的时候获取到的。

你现在可以放心的使用Order实例,你甚至可以访问Employee和Customer的实例,这时什么也不会发生,不会有查询操作。但是,注意,当你去访问代理实例的非标识符成员(也就是属性和方法)的时候,Nhibernate为了确保你请求的非标识符成员有数据,它会去数据库请求数据。在这里就是当你去访问Employee和Customer的属性和方法的时候,Nhibernate会去数据库请求你想要的数据。那Nhibernate是怎么实现的这种机制呢? 因为代理会重写你的实体类(Entity)的属性和方法,当属性和方法被调到的时候,Nhibernate会检查,如果当前实例的数据已经存在了,它就只会调用实体类(Entity)中属性和方法的实现,如果没有数据,它会先去获取数据,然后再调用实体类(Entity)中属性和方法的实现。

这是基本的面相对象思想,你的实体类(Entity)是Nhibernate 代理的基类。 这些代理(proxy)会为你的实体类(Entity)的行为上添加一些额外的行为。 Nhibernate需要重写所有的公共成员(public)来确保在合适的时间这些额外的行为能被触发。

为什么Nhibernate中属性和方法必须Virtual的的更多相关文章

  1. js中属性和方法的类型和区别

    对象的属性:私有属性(var).类属性(静态属性).对象属性(this).原型属性(prototype). 对象的方法: 私有方法(funtion).类方法(静态方法).对象方法(this).原型方法 ...

  2. Java继承中属性、方法和对象的关系

    大家都知道子类继承父类是类型的继承,包括属性和方法!如果子类和父类中的方法签名相同就叫覆盖!如果子类和父类的属性相同,父类就会隐藏自己的属性! 但是如果我用父类和子类所创建的引用指向子类所创建的对象, ...

  3. python中类中属性和方法的具体定义方法和使用

    1. Python中类中特性分成属性和方法 属性和方法都分为私有和公有的,私有的只可以在本类中使用外部是无法访问的 2. 定义属性(成员变量)的语法格式(公有属性/私有属性) class 类名: de ...

  4. OC中属性及方法

    1.声明式属性    a.实例变量    b.声明属性        自动生成setter/getter方法        .h ->@property 属性类型 属性名;        .m ...

  5. php中属性和方法的修饰符

    <?php class A{ private function do1(){ echo "do1 called"; } protected function do2(){ e ...

  6. Python中类的方法属性与方法属性的动态绑定

    最近在学习python,纯粹是自己的兴趣爱好,然而并没有系统地看python编程书籍,觉得上面描述过于繁琐,在网站找了一些学习的网站,发现廖雪峰老师的网站上面的学习资源很不错,而且言简意赅,提取了一些 ...

  7. Java 类、属性、方法修饰符 public、private、protected、default

    Java 中修饰类修饰符:public .default (默认) Java 中修饰类中属性.方法修饰符:public.private.protected.default (默认) 通过 IDEA 创 ...

  8. java利用反射访问类的私有(private)属性及方法

    Java语言中,在一个类中,为了不让外界访问到有的属性和方法,通常将其设置为private,用正常的方式(对象名.属性名,对象名.方法名)将无法访问此属性与方法,但有没有其他方法可以访问呢?答案是有的 ...

  9. 在 NHibernate 中一切必须是 Virtual 的吗?

    原文地址:Must Everything Be Virtual With NHibernate? 老赵在博文中 我对NHibernate的感受(2):何必到处都virtual 提到这篇文章,顺便翻译一 ...

随机推荐

  1. 父页面iframe自动适应子页面的宽高度

    <script type="text/javascript"> function load(){ parent.document.getElementById(&quo ...

  2. RMQ问题与ST算法

    RMQ(Range Minimum/Maximum Query)问题是求区间最值问题. 对于长度为 n 的数组 A,进行若干次查询,对于区间 [L,R] 返回数组A中下标在 [L,R] 中的最小(大) ...

  3. C# 引用SHDocVw 实现模拟网页操作

    因为最近项目需要,所以接触到了网页爬取. 1. HttpWebRequest 初期接触的都是一些比较简单的网页,通过Fiddler抓包分析后,就能模拟进行http请求,进行想要的操作. 2. WebB ...

  4. android使用adb installapk出现can't find **.apk to install

    1. 有时候我们需要在使用ADT命令的adb安卓外部命令的时候出现下面的问题 出现上面的问题,其实是我们的apk方的文件不对,具体是什么问题怎么改路径我就没有深入研究了,.我查阅了好多资料才发现并不是 ...

  5. UITextField 对输入金额的约束

    [2016/1/18更新] -- 五个人辛辛苦苦干了一年的项目终于上线了,今天有空看了一下正则表达式教程,然后开始rebuild之前的种种对字符串的约束,首先就从这个金额输入框开始吧,修改后的代码如下 ...

  6. 类库探源——System.String

    一.MSDN描述 String 类: 表示文本,即一系列的 Unicode 字符 命名空间 : System 程序集 : mscorlib.dll 继承关系: 备注: 1. 字符串是 Unicode ...

  7. Codevs 2549 自然数和分解

    2549 自然数和分解 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 白银 Silver 传送门 题目描述 Description 把自然数N分解为若干个自然数之和,输出方案数. 输 ...

  8. php 单引号与双引号区别

    一.单引号与双引号区别 1." "双引号里面的字段会经过编译器解释,然后再当作HTML代码输出. 2.' '单引号里面的不进行解释,直接输出. 从字面意思上就可以看出,单引号比双引 ...

  9. 从Bash漏洞学Shell脚本(冒号)

    前天,爆发了Bash安全漏洞,非常恐怖.在网络上开始飞速传播,附带了非常友好的检测工具. $ env x='() { :;}; echo vulnerable' bash -c "echo ...

  10. 原始的JDBC操作

    -----------------------------根据配置文件---------------------------- package cn.gdpe.jdbc; import java.io ...