zoukankan      html  css  js  c++  java
  • 一次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
    <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<ARTIST>>" %>

    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
        
    <fieldset>
            
    <legend>List</legend>
            
    <%= Html.Grid(Model).Columns(
                column =>{
                        
    column.For(c => c.Id).Named("ID");
                        column.For(c => c.Name).Named("名儿");
                        column.For(c => c.Belong).Named("在哪儿");
                        column.For(c => c.ALBUMS.Count).Named("专辑数");
                    })
                .Attributes(style => "90%")
                .RowStart(c => string.Format("<tr class='{0}'>", c.IsAlternate ? "alt" : ""))
                
    %>
        
    </fieldset>
    </asp:Content>

    七、运行,出现异常“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;

    十四、至此,OSIV成功覆盖整个mvc应用。凡事有利弊,在java社区会有这样的讨论:

    open session in view 在访问量很大的时候,容易造成页面假死现象

    “因为osiv会在每次请求的过程中占用一个session,如果这个请求过程太长,session就无法释放了。可以在tomcat前端加一个apache,讲网速太慢的请求隔离掉。”

     也许在这样的时候,一个好的网络运维,比你吭哧更管用 :)

    Creative Commons License
    本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议
    欢迎转载,但必须保留文章的署名老莫的帐本子
    并保留此链接:http://moye.cnblogs.com/
    如有疑问请发邮件:moyerock@gmail.com
  • 相关阅读:
    局域网无法访问vmware虚拟机WEB服务器解决办法
    zipimport.ZipImportError: can't decompress data; zlib not available 解决办法
    如何在win下使用linux命令
    《redisphp中文参考手册》php版
    Python关键字参数与非关键字参数(可变参数)详解
    Python与 PHP使用递归建立多层目录函数
    第一场个人图论专题
    poj_2762,弱连通
    word宏的问题
    fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  • 原文地址:https://www.cnblogs.com/moye/p/1611907.html
Copyright © 2011-2022 走看看