NHibernate, and odd “Session is Closed!” errors

南楼画角 提交于 2019-11-28 05:58:12

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

I ran into Session Closed/ObjectDisposedExceptions intermittently when running integration tests for a Web API project I was working on.

None of the tips here solved it, so I built a debug version of NHibernate to find out more, and I saw it doing a linq query when the controller's Get function was returning with the IEnumerable of response objects.

It turned out that our repository was doing a linq query and returning the IEnumerable of the domain objects, but never calling ToList() to force the evaluation of the query. The service in turn returned the IEnumerable, and the Controller wrapped the enumerable with response objects and returned it.

During that return, the linq query finally got executed, but the NHibernate session got closed in the interim at some point, so it threw an exception. The solution was to always make sure we call .ToList() in the repository within the using session block before we return to the service.

Kent Boogaart

I suggest you set a breakpoint on SessionImpl.Close / SessionImpl.Dispose and see who is calling it via the stack trace. You could also just build a debug version of NH for yourself and do the same.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!