代码之家  ›  专栏  ›  技术社区  ›  Sekhat

NHibernate和odd“会话已结束!”错误

  •  16
  • Sekhat  · 技术社区  · 15 年前

    注意:现在我已经打印出来了,我不得不为这个超长的问题道歉,但是,我认为这里提供的所有代码和信息在某种程度上是相关的。


    好吧,在我的ASP.NETWebForms应用程序中随机出现“会话已关闭”错误。然而,今天,它终于一次又一次地发生在同一个地方。我几乎可以肯定的是,在我的代码中没有任何东西在处理或关闭会话,因为使用的代码位被很好地包含在所有其他代码之外,您将在下面看到。

    好吧,那么,我的第一个 SessionFactoryProvider SessionProvider 课程:


    public class SessionFactoryProvider : IDisposable
    {
        ISessionFactory sessionFactory;
    
        public ISessionFactory GetSessionFactory()
        {
            if (sessionFactory == null)
                sessionFactory =
                    Fluently.Configure()
                            .Database(
                                MsSqlConfiguration.MsSql2005.ConnectionString(p =>
                                    p.FromConnectionStringWithKey("QoiSqlConnection")))
                            .Mappings(m =>
                                m.FluentMappings.AddFromAssemblyOf<JobMapping>())
                            .BuildSessionFactory();
    
            return sessionFactory;
        }
    
        public void Dispose()
        {
            if (sessionFactory != null)
                sessionFactory.Dispose();
        }
    }
    

    会话提供程序

    public class SessionProvider : IDisposable
    {
        ISessionFactory sessionFactory;
        ISession session;
    
        public SessionProvider(SessionFactoryProvider sessionFactoryProvider)
        {
            this.sessionFactory = sessionFactoryProvider.GetSessionFactory();
        }
    
        public ISession GetCurrentSession()
        {
            if (session == null)
                session = sessionFactory.OpenSession();
    
            return session;
        }
    
        public void Dispose()
        {
            if (session != null)
            {
                session.Dispose();                
            }
        }
    }
    

    这两个类与Ninject连接起来,如下所示:

    NHibernateModule公司

    public class NHibernateModule : StandardModule
    {        
        public override void Load()
        {
            Bind<SessionFactoryProvider>().ToSelf().Using<SingletonBehavior>();
            Bind<SessionProvider>().ToSelf().Using<OnePerRequestBehavior>();
        }
    }
    

    据我所知,工作和预期一样。

    BaseDao<T> 班级:


    BaseDao公司

    public class BaseDao<T> : IDao<T> where T : EntityBase
    {
        private SessionProvider sessionManager;
        protected ISession session { get { return sessionManager.GetCurrentSession(); } }
    
        public BaseDao(SessionProvider sessionManager)
        {
            this.sessionManager = sessionManager;
        }        
    
        public T GetBy(int id)
        {
            return session.Get<T>(id);
        }
    
        public void Save(T item)        
        {
            using (var transaction = session.BeginTransaction())
            {
                session.SaveOrUpdate(item);
    
                transaction.Commit();
            }
        }
    
        public void Delete(T item)
        {
            using (var transaction = session.BeginTransaction())
            {
                session.Delete(item);
    
                transaction.Commit();
            }
        }
    
        public IList<T> GetAll()
        {
            return session.CreateCriteria<T>().List<T>();
        }
    
        public IQueryable<T> Query()
        {
            return session.Linq<T>();
        }        
    }
    

    它是这样装订在一起的:


    道模

    public class DaoModule : StandardModule
    {
        public override void Load()
        {
            Bind(typeof(IDao<>)).To(typeof(BaseDao<>))
                                .Using<OnePerRequestBehavior>();
        }
    }
    

    导致这种情况的web请求是在我保存对象时发生的,直到今天我对模型做了一些更改之后才发生,但是对模型的更改并没有改变数据访问代码。虽然它改变了一些NHibernate映射(如果有人感兴趣,我也可以发布这些)

    据我所知, BaseDao<SomeClass>.Get 那就叫什么 BaseDao<SomeOtherClass>.Get BaseDao<TypeImTryingToSave>.Save 被称为。

    这是我接到的第三个电话 Save()

    using (var transaction = session.BeginTransaction())
    

    以“会话已关闭!”或者说是例外:

    Session is closed!
    Object name: 'ISession'.
    
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
    
    Exception Details: System.ObjectDisposedException: Session is closed!
    Object name: 'ISession'.
    

    实际上,在调试器上的后续操作显示了第三次从调试器请求会话 它确实是封闭的,没有连接。

    我已经证实了 Dispose 在我的 在我的 会话提供程序 Save 召唤我的刀。

    所以现在我有点困了。我突然想到了几件事。

    • 我做错什么了吗?
    • 有什么解决办法或想法吗?

    提前谢谢

    3 回复  |  直到 15 年前
        1
  •  19
  •   Jamie Ide    15 年前

    ASP.NET是多线程的,因此访问ISession必须是线程安全的。假设每个请求都使用session,最简单的方法就是使用NHibernate的内置处理 contextual sessions .

    首先配置NHibernate以使用web会话上下文类:

    sessionFactory = Fluently.Configure()
        .Database(
            MsSqlConfiguration.MsSql2005.ConnectionString(p =>
                p.FromConnectionStringWithKey("QoiSqlConnection")))
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<JobMapping>())
        .ExposeConfiguration(x => x.SetProperty("current_session_context_class", "web")
        .BuildSessionFactory();
    

    然后使用 ISessionFactory.GetCurrentSession()

        public ISession GetContextSession()
        {
            var factory = GetFactory(); // GetFactory returns an ISessionFactory in my helper class
            ISession session;
            if (CurrentSessionContext.HasBind(factory))
            {
                session = factory.GetCurrentSession();
            }
            else
            {
                session = factory.OpenSession();
                CurrentSessionContext.Bind(session);
            }
            return session;
        }
    
        public void EndContextSession()
        {
            var factory = GetFactory();
            var session = CurrentSessionContext.Unbind(factory);
            if (session != null && session.IsOpen)
            {
                try
                {
                    if (session.Transaction != null && session.Transaction.IsActive)
                    {
                        session.Transaction.Rollback();
                        throw new Exception("Rolling back uncommited NHibernate transaction.");
                    }
                    session.Flush();
                }
                catch (Exception ex)
                {
                    log.Error("SessionKey.EndContextSession", ex);
                    throw;
                }
                finally
                {
                    session.Close();
                    session.Dispose();
                }
            }
        }        
    
        2
  •  7
  •   honk    10 年前

    在为正在处理的webapi项目运行集成测试时,我间歇性地遇到Session Closed/ObjectDisposedExceptions。

    这里的技巧都没有解决这个问题,所以我构建了一个NHibernate的调试版本来了解更多信息,我看到它在控制器运行时执行linq查询 Get 函数正在返回 IEnumerable

    结果发现,我们的存储库正在执行linq查询并返回 I可数 域对象,但从不调用 ToList() 强制计算查询。服务人员又返回了 I可数 ,控制器用响应对象包装可枚举对象并返回它。

    在这个返回过程中,linq查询最终被执行,但是NHibernate会话在某个时候被关闭了,所以它抛出了一个异常。解决办法是确保我们打电话 .ToList() 在我们返回服务之前,在存储库中的using session块中。

        3
  •  3
  •   Tim Cooper    13 年前

    set a breakpoint SessionImpl.Close / SessionImpl.Dispose 并通过堆栈跟踪查看是谁在调用它。您也可以为自己构建一个NH的调试版本,并执行相同的操作。