为什么Nhibernate中属性和方法必须Virtual的

如果你曾经用过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的

时间: 2024-08-08 09:08:06

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

面向对象中对象和类以及如何访问类中属性和方法

对象:客观存在的具体事物.某一个具体的个体 *类:具有相同行为和共同特征的对象的集合,类是人类脑海中一个抽象的概念,通过类创建对象 *类中的成员:1.成员属性(描述外部特征) 2.成员方法(描述功能行为)* 如何定义一个类: * [修饰符] class 类名{ * //1.属性的定义:与定义变量类似 * [修饰符] 数据类型 属性名; * //2.方法的定义 * [修饰符] 返回值类型 方法名(形参列表){ * //方法体; * } * } * 成员变量和局部变量的区别: * 1.作用域不同:成

Python3基础 一个类中 属性与方法重名时,属性会覆盖方法 基本示例

镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.------------------------------------------ code: class MyClass : #属性 num=1 #方法 def num(self): print('hello') test=MyClass() #这里还可以呢 test.num() #下面对属性进行赋值 test.num=1a #你会发现 方法报错了 test.num()

python中属性和方法的动态绑定

# 定义一个类 class Student(object):     pass # 实例化一个对象     s = Student() # 给这个对象绑定一个属性name s.name = 'John' print(s.name) John # 定义一个方法 def set_age(self, age):     self.age = age # 导入模块     from types import MethodType #给s这个对象绑定一个set_age的方法 s.set_age = Met

OC中属性及方法

1.声明式属性    a.实例变量    b.声明属性        自动生成setter/getter方法        .h ->@property 属性类型 属性名;        .m ->@synthesize 属性名 = 实例变量名;    c.点语法2.IOS5中的属性    a.实例变量  省        会自动生成实例变量,实例变量名 等于 _属性名    b.声明属性        @property 属性类型 属性名;        @synthesize 属性名 =

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

最近在学习python,纯粹是自己的兴趣爱好,然而并没有系统地看python编程书籍,觉得上面描述过于繁琐,在网站找了一些学习的网站,发现廖雪峰老师的网站上面的学习资源很不错,而且言简意赅,提取了一些python中的重要的语法和案例.重要的是可以在线测试python的运行代码,缺点就是没有系统的看python的书籍,不能及时的将知识的碎片化联系在一起,这也是看书与不看书的区别.尤其是在python类与实例的方法的调用中觉得云里雾里,思考之后将自己的想法记录下,一来加深自己理解,巩固自己记忆,而来

全面理解Javascript中Function对象的属性和方法

函数是 JavaScript 中的基本数据类型,在函数这个对象上定义了一些属性和方法,下面我们逐一来介绍这些属性和方法,这对于理解Javascript的继承机制具有一定的帮助. 属性(Properties) arguments 获取当前正在执行的 Function 对象的所有参数,是一个类似数组但不是数组的对象,说它类似数组是因为其具有数组一样的访问性质及方式,可以由arguments[n]来访问对应的单个参数的值,并拥有数组长度属性length.还有就是arguments对象存储的是实际传递给

JavaScript 中关于Date的内置对象属性和方法的总结

Date 属性: 1.constructor    所建立对象的函数参考 2.prototype      能够为对象加入的属性和方法 方法: 1.getDay()    返回一周中的第几天(0-6) 2.getYear()    返回年份.2000年以前为2位,2000(包含)以后为4位 3.getFullYear()     返回完整的4位年份数 4.getMonth()      返回月份数(0-11) 5.getDate()       返回日(1-31) 6.getHours()  

Swift语言中的属性,方法,下标脚本以及继承

从这篇章节起,Swift编程语言指南大部分的重要内容在于概念,代码并不是太多.理解Swift的面向对象理念,语法以及类结构,构造析构过程对于很好的应用Swift语言将会有比较大的帮助. 属性 存储属性 存储属性通常是那些可以通过直接赋值,或者直接访问成员能够获得的属性类型. 它有些要注意的地方: 若一个结构体被声明为常量,则子属性无法被修改了.在Objective-C中,我们总是无法修改结构体的子属性,但是swift却可以,不过这种情况是个例外,当你存储型属性是个结构体并且是个常量,那你就不要再

OC中的属性、方法及内存管理

普通方法:关注(代表)对象可以”干什么”,过程中需要实例变量.-(void)show;输出 … 访问属性    属性:属性专门处理实例变量.(程序执行过程当中)    初始化方法:一创建对象(第一时间),给实例变量赋值.    工厂方法:只一个方法,帮我们解决创建对象问题.初始化对象的问题.工厂方法创建对象最简单.(对象达到一定量级) 补:在类方法中,可以调用类中创建的实例的实例方法或实例变量. 算法.设计模式:是人们总结的一种规律,一种思想. 1.单例模式    是一种特殊的工厂方法.