Проблемы NHibernate с приложением MVC для сеанса на запрос

Я написал C # MVC 3 с NHibernate в качестве ORM, и у меня возникают некоторые странные исключения при загрузке большинства страниц. Похоже, они в основном относятся к закрытым сессиям и т.п., и я проверил большинство распространенных проблем, но мало что помогло. Некоторые исключения включают:

    [[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. У меня даже было исключение «Сеанс закрыт» в методе BeginTranscation в моем файле Global.asax.

Приложение использует веб-параметр current_session_context_class в hibernate.cfg.xml.

Я подозреваю, что это связано с моим кодом управления сеансом. Веб-сайт обычно загружает около 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. Иногда сеансы не запускаются с помощью BeginTranscation, иногда они не фиксируются, иногда нет; и, что самое удивительное, иногда они запускаются трижды, но не заканчиваются.

Любые вызовы объекта 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 без их сброса, фиксации, удаления сеанса и повторного открытия в соответствии с приведенным выше кодом. Мы будем очень признательны за любой свет, который вы могли бы пролить на это!


person James    schedule 21.01.2013    source источник


Ответы (1)


Кажется, вы храните свой сеанс в глобальном (для всего приложения) свойстве в файле Global.asax.cs. Это означает, что свойство содержит последний созданный сеанс, а не сеанс для определенного запроса. Два запроса одновременно, и вы даже не знаете, получаете ли вы доступ к только что созданному сеансу, потому что он мог уже быть перезаписан другим сеансом из следующего запроса с тем же кодом. Вы не должны хранить свою сессию где-нибудь, не связанное с веб-запросом, если вы хотите следовать шаблону Session-per-Request. Например, вы можете сохранить сеанс NH в HttpContext.Items-Collection. В качестве еще одного способа управления сеансами в MVC Айенде опубликовал приятный пример того, как обернуть управление сеансом вокруг одного действия MVC, а не всего запроса.

person Dirk Trilsbeek    schedule 21.01.2013