Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

NHibernate延迟加载机制 - NHibernate 2.1.2

动态代理的使用方法和相关概念可以参考Castle Dynamic Proxy
NHibernate实现延迟加载的主要结构:
   
BuildSessionFactory的时候:
1. 根据proxyfactory.factory_class的配置创建IProxyFactoryFactory对象
2. 如果use_proxy_validator配置为true(默认为true),加载实体映射元数据之后,对需要代理的实体使用IProxyFactoryFactory的ProxyValidator进行验证(例如要求实体有默认构造器、方法属性必须为virtual类型等)
3. 为需要代理的实体类型创建IProxyFactory,并调用IProxyFactory对象的PostInstantiate方法
    对每一个需要代理的实体类型都会创建一个IProxyFactory对象并缓存,创建代理对象则通过IProxyFactory的GetProxy方法完成
    AbstractProxyFactory主要是实现PostInstantiate方法,这个方法主要是将创建代理对象所需的信息记录下来,例如EntityName(字符串的实体类名)、PersistentClass(代理对象的System.Type)、Interfaces(代理对象需要额外实现的接口,例如INHibernateProxy),其他几个记录的属性则为单主键和组合主键id的get、set方法
    ProxyFactory则主要实现GetProxy方法,使用Castle或者LinFu等动态代理库以及上述信息创建代理对象

创建代理对象:
IProxyFactory.GetProxy方法完成
1. 创建一个LazyInitializer对象。LazyInitializer实现了各个动态代理类库的拦截器接口
2. 通过Castle、LinFu、Spring等动态代理库创建class proxy或者interface proxy的代理对象
    创建代理对象时INHibernateProxy作为一个mixin的接口,NHibernate内部用这个接口来区分代理对象和真实对象。对这个接口的方法调用则在拦截器中处理

延迟加载:
创建代理对象时,均使用NHibernate.ByteCode.LinFu或者NHibernate.ByteCode.Castle中的LazyInitializer类作为拦截器,因此对代理对象的方法调用都在LazyInitializer中拦截处理
ILazyInitializer接口主要用于延迟加载的相关处理
首先,对代理对象某些方法的调用不会触发延迟加载行为,比如读取主键的值、组合类型属性的主键值、没有override的情况下调用Object基类的一些方法以及Dispose方法等,这些逻辑在BasicLazyInitializer的Invoke方法中处理
当调用代理对象的其他方法时,触发延迟加载行为。加载处理在AbstractLazyInitializer的Initialize方法中完成,加载实体所需要的信息(主要有session、id、实体的类型)在创建LazyInitializer对象时均已经提供,加载过程仍然使用一级缓存、二级缓存、数据库这样一个加载顺序

加载的实体对象与代理对象是两个独立的对象,NHibernate并没有将加载后的实体属性值设置到代理对象上,估计一是考虑到有interface proxy存在的情况,另外实际加载过程是比较独立的,他会重新创建一个真实的实体对象,会放入一级、二级缓存中,还有一点,这种处理方式NHibernate以及client都可以通过代理对象来得到加载后的真实对象。真实的实体对象保存在AbstractLazyInitializer的Target属性中,完成加载以后,拦截器中通过反射来调用target对象的方法

下面代码示例怎么由代理对象得到真实对象(非NHibernate官方公布的方法,慎用):
ISessionFactory sf = new Configuration().Configure().BuildSessionFactory();
using (ISession session = sf.OpenSession())
{
    //仅创建代理对象,没有实际加载
    MyUser proxy = session.Load<MyUser>(11);
    Console.WriteLine(proxy.GetType().FullName);
    //因为override了ToString方法,因此下面这个调用将触发加载行为
    Console.WriteLine(proxy.ToString());
    //获取真实对象
    NHibernate.Proxy.INHibernateProxy nhProxy = proxy as INHibernateProxy;
    MyUser real = nhProxy.HibernateLazyInitializer.GetImplementation() as MyUser;
    Console.WriteLine(real.GetType().FullName);
    Console.WriteLine(real.ToString());
}
sf.Close();
Console.ReadKey();
下面是运行结果:
   

posted on 2010-03-18 23:16 riccc 阅读(...) 评论(...)