一次OSIV调试经历

使用nHibernate有一段时间了,但一直没有使用它的延迟加载机制,究其根本大概源于刚接触nHibernate的失败调试经历——因为总是看到诸如“Could not initialize proxy - the owning Session was closed.”或者线程没有绑定Session之类的异常:

 

实际上园子里提供了不少解决这类问题的方案,如李永京的 NHibernate之旅(13):初探立即加载机制 等, 但终归觉得不够理想——较之各种方案描述的单一环境,既有框架是构建在spring.net之上的,意味着对待Session不进行侵入式管理,更进一步,spring.net对于Session的请求响应要覆盖到所有View。

 

直到最近,重新拜读了 NHibernate之旅(9):探索父子关系(一对多关系) ,意识到关联对象的加载是个问题,决定还是得把延迟加载用起来,事关性能。

经 刘冬 先生的指点,知道了spring.net有一种管理Session以用于lazy-load的模式——Open Session In View(OSIV),原理大致是

 

spring.net提供了一个HttpModule,“就是一个filter,每次request进来,就打开一个session放到ThreadLocal里,以后用到session就拿出来用,filter结束的时候,再清空ThreadLocal,关闭session”

 OSIV模式一旦开启,lazy-load所需Session的绑定生灭,便不再需要开发者操心,听着确实很爽,于是便开始了长达一周噩梦般的调试:

 一、先更新一下spring.netnHibernate版本,分别是1.3.0RC和2.1.1GA。

 二、更新各层引用 编译报错: 

 

错误 34 “NHibernate.Engine.ISessionFactoryImplementor”不包含“CloseConnection”的定义,并且找不到可接受类型为“NHibernate.Engine.ISessionFactoryImplementor”的第一个参数的扩展方法“CloseConnection”(是否缺少 using 指令或程序集引用?) .../DaoTemplate.cs 833 21 woodigg.DAO

在nH 2.1中,ISessionFactoryImplementor接口不再包含CloseConnection方法,将其注释,并查找了一下DaoTemplate中所有关于Session.Close的代码,全部注释(在OSIV中,不再干预Session的生命周期)。编译通过。 

三、修改配置文件中的HibernateSessionFactory对象,在HibernateProperties中添加这样一个条目:

appDaoConfig.xml
<!-- 动态代理 -->
< entry  key ="proxyfactory.factory_class"
                    value
="NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle"   />

 同时引用nHibernate.Bytecode.Castle和Castle.DynamicProxy2程序集。

四、修改web.config,注册OSIV模块,并让其作用于HibernateSessionFactory对象: 

web.config
< appSettings >
    
<!-- SessionFactory -->
    
< add  key ="Spring.Data.NHibernate.Support.OpenSessionInViewModule.SessionFactoryObjectName"  value ="HibernateSessionFactory" />
</ appSettings >

< httpModules >
    
<!-- OpenSessionInView -->
    
< add  name ="OpenSessionInView"  type ="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate21" />
</ httpModules >

 五、在Mvc2站点中添加一个View,取一个拥有关联集合子对象的数据列表(如下图,ARTIST对象有一个ALBUMS关联对象,它是ALBUM对象的集合,一对多):

 此时使用的搜索代码来自DaoTemplate,依赖于spring.net提供的HibernateTemplate

DaoTemplate.cs
IList  alist  =  HibernateTemplate.Find(hql); 

六、使用MvcContrib的Grid进行绑定: 

Index.aspx

七、运行,出现异常“Could not initialize proxy - no Session.

八、查看Log4net日志,有这么一段:

2009-11-20 10:03:44,343 [1] DEBUG NHibernate.Transaction.AdoTransaction [(null)] - IDbTransaction disposed.
2009-11-20 10:03:44,343 [1] DEBUG NHibernate.Impl.SessionImpl [(null)] - closing session
2009-11-20 10:03:44,343 [1] DEBUG NHibernate.AdoNet.AbstractBatcher [(null)] - running BatcherImpl.Dispose(true)
2009-11-20 10:03:47,140 [1] ERROR NHibernate.LazyInitializationException [(null)] - Initializing[Ic.Model.ALBUM#1]-Could not initialize proxy - no Session.

这说明一个问题:HibernateTemplate把Session关闭了

九、弃用HibernateTemplate,写一个抽象类,把SessionFactory植入后,只负责调用活动的Session:

HibernateDao.cs
using  NHibernate;

namespace  woodigg.DAO
{
    
///   <summary>
    
///  Base class for data access operations.
    
///   </summary>
     public   abstract   class  c    {
        
private  ISessionFactory sessionFactory;

        
///   <summary>
        
///  Session factory for sub-classes.
        
///   </summary>
         public  ISessionFactory SessionFactory
        {
            
protected   get  {  return  sessionFactory; }
            
set  { sessionFactory  =  value; }
        }

        
///   <summary>
        
///  Get's the current active session. Will retrieve session as managed by the 
        
///  Open Session In View module if enabled.
        
///   </summary>
         protected  ISession CurrentSession
        {
            
get
            {
                
return  sessionFactory.GetCurrentSession();
            }
        }
    }
}

十、将DaoTemplate的父类由HibernateDaoSupport改为HibernateDao,配置关系不用变化:

appDaoConfig.xml
< object  id ="DaoTemplate"  type ="woodigg.DAO.DaoTemplate, woodigg.DAO" >
   
< property  name ="SessionFactory"  ref ="HibernateSessionFactory"   />
</ object >

十一、再次运行,这次抛出的异常是(图1):

“No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here. ”

异常来自于sessionFactory.GetCurrentSession()方法,它无法为当前请求提供Session。这意味着:OSIV在MVC中没有生效。

 十二、从此时起,走上了弯路:

  1. 以为OSIV需要提供一个初始化的Session供其调用,遂将CurrentSession做成了单例,但其后发生了“access the same ISession in two concurrent thread” 这样的错误;
  2.  依旧使用sessionFactory.GetCurrentSession(),使用Spring.Transaction.Interceptor.TransactionInterceptor 将 事务管理器Spring.Data.NHibernate.HibernateTransactionManager 引入到 DaoTemplate中,这样做确实见效了,运行伊始就有一个Session,但lazy-load失败了,所有关联对象均无法加载,在运行时监视栏里查看它们,都形如“proxy223355124...”。 很明显,Session完成任务就撤了!

十三、冷静下来想想,其实从始至终,OSIV都没有生效过。那究竟是什么导致的呢?会不会是OpenSessionInViewModule没有被spring.net引入到view中?

所有的线索,指向了那个一直被信任的控制器工厂SpringControllerFactory ,看看它是怎么实现的:

 

SpringControllerFactory.cs
public   class  SpringControllerFactory : IControllerFactory
{
//..
        private static DefaultControllerFactory defalutf = null;
        
public IController CreateController(RequestContext context, string name)
        
{
            IApplicationContext configContext
                
= new XmlApplicationContext(ParameterFactory.CfgFilePath,
                    ParameterFactory.CfgBusinessFilePath, ParameterFactory.CfgControllersFilePath,
                    ParameterFactory.CfgServicesFilePath);
            
string controllName = GetFirstUpcaseName(name) + "Controller";           
            
if (configContext.ContainsObject(controllName))
            
{
                
return (IController)configContext.GetObject(controllName);
            }

            
else
            
{
                
if (defalutf == null)
                
{
                    defalutf 
= new DefaultControllerFactory();
                }

                
return defalutf.CreateController(context, name);
            }

        }

//..
}

 杯具啊!这个一年前的产物,在使用IApplication这个问题上手法笨拙,依靠预定义的配置文件路径引入环境。也许在CS结构中会有用处,但在BS环境中,这样更直接更全面(文件引用,实际没把包含OSIV配置的web.config引入环境,view扑空):

 

SpringControllerFactory.cs
WebApplicationContext configContext  =
                ContextRegistry.GetContext() 
as  WebApplicationContext;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值