如果你曾经用过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. table完美css样式,table的基本样式,table样式

    table完美css样式,table的基本样式,table样式 >>>>>>>>>>>>>>>>> ...

  2. C#一般处理程序获取Session

    如果需要用ajax去动态校验验证码,如何获取Session保存的值呢? 你需要做两步: 一.在你的一般处理程序中添加命名空间 (using System.Web.SessionState;) 二.在你 ...

  3. android中ListView控件

    今天学习了ListView控件和页面跳转,下面大致介绍下: 第一步:创建显示内容的文件vlist.xml: <?xml version="1.0" encoding=&quo ...

  4. C# Ref 与out 的区别

    在C#中,有四种传递参数方式: 1. 传值 (value) : 无额外修饰符 2. 传址(reference) : 需修饰符Ref,传入函数的参数必须先赋值 3. 输出参数(output): 需修饰符 ...

  5. 用数据库管理SERV-U的用户时,如何修改密码及原理

    将serv-u设置成数据库管理用户的前提是先要建立数据源 如何用Serv-U连接ODBC|1.可以在 FTP所在服务器安装一个 SQL Server  数据库,也可以使用论坛自带的数据库.建议在 FT ...

  6. Linq- ExcuteQuery用法

    DataContext.ExecuteQuery<TResult> 方法 (String, Object[]) 语法: public IEnumerable<TResult> ...

  7. Java:Date、Calendar、Timestamp的使用

    一.Java.util.Date 该对象包含了年月日时分秒信息.具体使用如下代码: //String 转换为Date private static void dateDemo() throws Par ...

  8. Masonry等比缩放

    第一种: CGFloat width = CGRectGetWidth([[UIScreen mainScreen] bounds]);         CGFloat aspectRatio = 1 ...

  9. jQuery easyUI框架中经常出现的问题

    相信开发者对于我们jquery来说都不会陌生吧,jquery为我们的开发提供了很多各式各样的库,满足各种开发的需求,其中我们知道的有轻量级的,但是也有一些基于富客服端的一些重量级库,顾名思义,当我们在 ...

  10. 打印出不同顺序的字符串&单引号和双引号的差异

    发现一个很好玩的打印顺序 package com.liaojianya.chapter1; /** * This program demonstrates the string. * @author ...