NHibernate 和奇怪的“会话已关闭!"错误 [英] NHibernate, and odd "Session is Closed!" errors

查看:21
本文介绍了NHibernate 和奇怪的“会话已关闭!"错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:既然我已经输入了这个,我必须为这个超长的问题道歉,但是,我认为这里提供的所有代码和信息在某种程度上都是相关的.><小时>

好的,我在我的 ASP.NET webforms 应用程序中的随机点收到奇怪的会话已关闭"错误.然而,今天,它终于在同一个地方一遍又一遍地发生.我几乎可以肯定,在我的代码中没有任何内容处理或关闭会话,因为使用的代码位与所有其他代码完全隔离,如下所示.

我也在使用 ninject 作为我的 IOC,这可能/可能不重要.

好的,首先是我的 SessionFactoryProviderSessionProvider 类:

<小时>

SessionFactoryProvider

公共类 SessionFactoryProvider : IDisposable{ISessionFactory sessionFactory;公共 ISessionFactory GetSessionFactory(){如果(会话工厂 == 空)会话工厂 =Fluently.Configure().数据库(MsSqlConfiguration.MsSql2005.ConnectionString(p =>p.FromConnectionStringWithKey("QoiSqlConnection"))).映射(m =>m.FluentMappings.AddFromAssemblyOf()).BuildSessionFactory();返回会话工厂;}公共无效处置(){如果(会话工厂!= null)sessionFactory.Dispose();}}

<小时>

SessionProvider

公共类 SessionProvider : IDisposable{ISessionFactory sessionFactory;ISession 会话;公共会话提供者(SessionFactoryProvider sessionFactoryProvider){this.sessionFactory = sessionFactoryProvider.GetSessionFactory();}公共 ISession GetCurrentSession(){如果(会话==空)session = sessionFactory.OpenSession();返回会话;}公共无效处置(){如果(会话!= null){session.Dispose();}}}

<小时>

这两个类与 Ninject 连接如下:

NHibernateModule

公共类 NHibernateModule : StandardModule{公共覆盖无效负载(){绑定().ToSelf().Using();绑定().ToSelf().Using();}}

<小时>

据我所知,工作按预期进行.

现在我的 BaseDao 类:

<小时>

BaseDao

公共类BaseDao:IDao<T>其中 T : EntityBase{私人 SessionProvider sessionManager;受保护的 ISession 会话 { 获取 { 返回 sessionManager.GetCurrentSession();} }公共 BaseDao(SessionProvider sessionManager){this.sessionManager = sessionManager;}公共 T GetBy(int id){返回 session.Get(id);}public void Save(T 项){使用 (var 事务 = session.BeginTransaction()){session.SaveOrUpdate(item);交易.提交();}}公共无效删除(T项){使用 (var 事务 = session.BeginTransaction()){session.Delete(item);交易.提交();}}公共 IList<T>得到所有(){返回 session.CreateCriteria().List();}公共 IQueryable询问(){返回会话.Linq ();}}

<小时>

在 Ninject 中像这样绑定:

<小时>

DaoModule

公共类 DaoModule : StandardModule{公共覆盖无效负载(){绑定(typeof(IDao<>)).To(typeof(BaseDao<>)).使用();}}

<小时>

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

据我所知,BaseDao.Get 被调用,然后 BaseDao.Get 被调用,然后 BaseDao.Save 被调用.

这是 Save()

行中的第三次调用

using (var transaction = session.BeginTransaction())

失败并显示会话已关闭!"或者更确切地说是例外:

会话已关闭!对象名称:'ISession'.说明:在执行当前 Web 请求期间发生未处理的异常.请查看堆栈跟踪以获取有关错误及其在代码中的来源的更多信息.异常详细信息:System.ObjectDisposedException:会话已关闭!对象名称:'ISession'.

实际上,在调试器上进行的操作显示第三次从 SessionProvider 请求会话时,它确实已关闭且未连接.

我已经验证在我的 SessionFactoryProvider 和我的 SessionProvider 上的 Dispose 是在请求​​结束时而不是在 之前调用的>保存调用是在我的Dao上进行的.

所以现在我有点卡住了.想到了一些事情.

  • 我是否明显做错了什么?
  • NHibernate 是否会在没有我要求的情况下关闭会话?
  • 关于我可能会做什么的任何解决方法或想法?

提前致谢

解决方案

ASP.NET 是多线程的,因此对 ISession 的访问必须是线程安全的.假设您使用 session-per-request,最简单的方法是使用 NHibernate 的内置处理 上下文会话.

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

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

然后使用 ISessionFactory.GetCurrentSession() 获取现有会话,如果不存在,则将新会话绑定到工厂.下面我将剪切+粘贴用于打开和关闭会话的代码.

 public ISession GetContextSession(){var factory = GetFactory();//GetFactory 在我的助手类中返回一个 ISessionFactoryISession 会话;如果(CurrentSessionContext.HasBind(工厂)){session = factory.GetCurrentSession();}别的{session = factory.OpenSession();CurrentSessionContext.Bind(session);}返回会话;}公共无效 EndContextSession(){var factory = GetFactory();var session = CurrentSessionContext.Unbind(factory);如果(会话!= null && session.IsOpen){尝试{if (session.Transaction != null && session.Transaction.IsActive){session.Transaction.Rollback();throw new Exception("回滚未提交的 NHibernate 事务.");}session.flush();}捕获(异常前){log.Error("SessionKey.EndContextSession", ex);扔;}最后{会话.关闭();session.Dispose();}}}

Note: Now that I've typed this out, I have to apologize for the super long question, however, I think all the code and information presented here is in some way relevant.


Okay, I'm getting odd "Session Is Closed" errors, at random points in my ASP.NET webforms application. Today, however, it's finally happening in the same place over and over again. I am near certain that nothing is disposing or closing the session in my code, as the bits of code that use are well contained away from all other code as you'll see below.

I'm also using ninject as my IOC, which may / may not be important.

Okay, so, First my SessionFactoryProvider and SessionProvider classes:


SessionFactoryProvider

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();
    }
}


SessionProvider

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();                
        }
    }
}


These two classes are wired up with Ninject as so:

NHibernateModule

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


and as far as I can tell work as expected.

Now my BaseDao<T> class:


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>();
    }        
}


Which is bound in Ninject like so:


DaoModule

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


Now the web request that is causing this is when I'm saving an object, it didn't occur till I made some model changes today, however the changes to my model has not changed the data access code in anyway. Though it changed a few NHibernate mappings (I can post these too if anyone is interested)

From as far as I can tell, BaseDao<SomeClass>.Get is called then BaseDao<SomeOtherClass>.Get is called then BaseDao<TypeImTryingToSave>.Save is called.

it's the third call at the line in Save()

using (var transaction = session.BeginTransaction())

that fails with "Session is Closed!" or rather the exception:

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'.

And indeed following through on the Debugger shows the third time the session is requested from the SessionProvider it is indeed closed and not connected.

I have verified that Dispose on my SessionFactoryProvider and on my SessionProvider are called at the end of the request and not before the Save call is made on my Dao.

So now I'm a little stuck. A few things pop to mind.

  • Am I doing anything obviously wrong?
  • Does NHibernate ever close sessions without me asking to?
  • Any workarounds or ideas on what I might do?

Thanks in advance

解决方案

ASP.NET is multi-threaded so access to the ISession must be thread safe. Assuming you're using session-per-request, the easiest way to do that is to use NHibernate's built-in handling of contextual sessions.

First configure NHibernate to use the web session context class:

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();

Then use the ISessionFactory.GetCurrentSession() to get an existing session, or bind a new session to the factory if none exists. Below I'm going to cut+paste my code for opening and closing a session.

    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();
            }
        }
    }        

这篇关于NHibernate 和奇怪的“会话已关闭!"错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆