3

我编写了一个使用 NHibernate 作为 ORM 的 C# MVC 3,并且在大多数页面加载时都抛出了一些奇怪的异常。它们似乎主要与封闭会话等有关,我已经检查了大多数常见问题,但发现几乎没有帮助。一些例外情况包括:

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
   at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.Data.SqlClient.SqlException (0x80131904): The server failed to resume the transaction. Desc:3b00000006.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.NullReferenceException: Object reference not set to an instance of an object.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.Data.SqlClient.SqlException (0x80131904): The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: The transaction is either not associated with the current connection or has been completed.
       at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.InvalidOperationException: SqlConnection does not support parallel transactions.

我为异常之墙道歉,我怀疑它们是相关的,但代码中可能存在另一个错误,也可能导致一两个错误。我不喜欢对这些东西使用随机这个词,但我似乎无法追踪调用它们的任何特定代码行,它们似乎只是出现在与 ISession 对象相关的代码行中。我什至在 Global.asax 文件中的 BeginTranscation 方法上抛出了“会话已关闭”异常。

应用程序使用 hibernate.cfg.xml 中 current_session_context_class 的 web 选项。

我怀疑它与我的会话管理代码有关。该网站通常会同时加载大约 10 个 AJAX 请求,当同时加载多个页面时,错误似乎更频繁地发生。有两个会话工厂,一个用于每个正在使用的数据库。

这是我相关的 Global.asax 代码:

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            _sessionFactory = (new WebClientSessionManager()).MakeSessionFactory();
            _sessionFactoryNotWeb = ClientSessionManager.MakeSessionFactory();
        }
protected void Application_BeginRequest(object sender, EventArgs e)
        {
            _session = _sessionFactory.OpenSession();
            _sessionNotWeb = _sessionFactoryNotWeb.OpenSession();
            CurrentSessionContext.Bind(_sessionNotWeb);
            CurrentSessionContext.Bind(_session);
            _session.BeginTransaction();
            _sessionNotWeb.BeginTransaction();
        }
 protected void Application_EndRequest(object sender, EventArgs e)
        {
            //Same code is repeated for the _sessionFactoryNotWeb
            ISession session = CurrentSessionContext.Unbind(_sessionFactory);
            if (session != null)
            {
                if (session.Transaction.IsActive)
                {
                    try
                    {
                        session.Transaction.Commit();
                    }
                    catch
                    {
                        session.Transaction.Rollback();
                    }
                }
                try
                {
                    session.Dispose();
                }
                catch
                {

                }
            }

我查看了 NHibernate profiler 中运行的页面。有时会话不是以 BeginTranscation 开始的,有时它们不是 Committed,有时两者都不是;最令人费解的是,有时它们启动了三遍,但没有完成。

对 ISession 对象的任何调用都通过此代码进行管理(每个工厂都有一个):

public static ISession WebSession()
        {
            if (CurrentSessionContext.HasBind(MvcApplication._sessionFactory))
            {
                if (MvcApplication._sessionFactory.GetCurrentSession().IsOpen)
                {
                    return MvcApplication._sessionFactory.GetCurrentSession();
                }
                else
                {
                    log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Unbinding NHibernate session");
                    CurrentSessionContext.Unbind(MvcApplication._sessionFactory);
                    return WebSession();
                }
            }
            else
            {
                log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Initialising NHibernate session");
                var session = MvcApplication._sessionFactory.OpenSession();
                CurrentSessionContext.Bind(session);
                session.BeginTransaction();
                return session;
            }
        }

在整个应用程序中没有对 BeginTransaction 或 Commit 的调用,除非它们被刷新、提交、会话处理,然后按照上面的代码重新打开。你们能对此有所了解的任何线索将不胜感激!

4

1 回答 1

6

您似乎将会话存储在 Global.asax.cs 中的全局(应用程序范围)属性中。这意味着,该属性包含创建的最后一个会话,而不是特定请求的会话。同时有两个请求,您甚至不知道您是否仍然访问刚刚创建的会话,因为它可能已经被下一个请求的另一个会话覆盖,运行相同的代码。如果您想遵循 Session-per-Request 模式,则不应将会话存储在与 Web 请求无关的位置。例如,您可以将 NH 会话存储在 HttpContext.Items-Collection 中。作为 MVC 中会话管理的另一种方式,Ayende发布了一个很好的示例,说明如何围绕单个 MVC 操作而不是整个请求来包装会话管理。

于 2013-01-21T07:25:08.803 回答