Странные ошибки NHibernate - PullRequest
1 голос
/ 08 ноября 2011

Я вижу некоторые странные ошибки NHibernate, которые периодически возникают в нашей производственной среде, но не на наших тестовых серверах.Дни пройдут без ошибок, затем мы получим ошибку, которая выглядит так, как показано ниже, и это похоже на то, что шлюзы открылись, и эти ошибки происходят повсюду в десятках различных частей нашего сайта.Единственный способ остановить их - сбросить пул приложений.

Мы открываем один сеанс и сопровождающую транзакцию для каждого входящего HTTP-запроса. Он хранится в коллекции HttpContext.Current.Items.Каждый из наших репозиториев расширяет базовый класс репозитория, который ссылается на ранее открытый сеанс.Вот модуль, который открывает и закрывает сеансы и транзакции:

public class NHibernateSessionModule : IHttpModule
{
    #region Public Methods
    /// <summary>
    /// Initializes the http module by hooking up the open session call to the begin
    /// request event and the close session call to the end request event.
    /// </summary>
    /// <param name="context">The context representing the current http request.</param>
    public void Init(HttpApplication context)
    {
        context.BeginRequest += ApplicationBeginRequest;
        context.EndRequest += ApplicationEndRequest;
        context.Error += OnError;
    }

    /// <summary>
    /// Disposes of the module.
    /// </summary>
    public void Dispose() { }
    #endregion

    #region Private Methods
    /// <summary>
    /// Fired when an error occurs during a request. Cleans up any open sessions and transactions.
    /// </summary>
    /// <param name="sender">The sender of the event.</param>
    /// <param name="e">The event arguments.</param>
    private static void OnError(object sender, EventArgs e)
    {
        var system = HttpContext.Current.Items["SystemSession"] as Lazy<ISession>;
        if (system != null && system.IsValueCreated && system.Value != null)
            RollbackSession(system.Value);
    }

    /// <summary>
    /// Fired as a request begins. Opens a session and accompanying transaction. Also authenticates the
    /// currently logged in user.
    /// </summary>
    /// <param name="sender">The sender of the event.</param>
    /// <param name="e">The event arguments.</param>
    private static void ApplicationBeginRequest(object sender, EventArgs e)
    {
        OpenSession("SystemSession", SessionFactoryProvider.SystemSessionFactory);
            }

    /// <summary>
    /// Opens a new session.
    /// </summary>
    /// <param name="key">The key used to store the opened session in the current http context.</param>
    /// <param name="sessionFactory">The factory used to open the session.</param>
    private static void OpenSession(string key, ISessionFactory sessionFactory)
    {
        if (HttpContext.Current.Items[key] != null)
            return;

        HttpContext.Current.Items[key] = new Lazy<ISession>(() => {
            var session = sessionFactory.OpenSession();
            session.BeginTransaction();
            return session;
        }, true);
    }

    /// <summary>
    /// Fired as a request ends. Closes the previously opened session and commits the session's transaction.
    /// </summary>
    /// <param name="sender">The sender of the event.</param>
    /// <param name="e">The event arguments.</param>
    private static void ApplicationEndRequest(object sender, EventArgs e)
    {
        var system = HttpContext.Current.Items["SystemSession"] as Lazy<ISession>;
        if (system != null && system.IsValueCreated && system.Value != null)
            CommitSession(system.Value);
    }

    /// <summary>
    /// Commits a session's transaction.
    /// </summary>
    /// <param name="session">The session.</param>
    private static void CommitSession(ISession session)
    {
        if (session == null)
            return;

        if (session.Transaction != null && session.Transaction.IsActive)
        {
            var transaction = session.Transaction;
            try
            {
                transaction.Commit();
            }
            catch (Exception)
            {
                transaction.Rollback();
            }
        }
        session.Close();
    }

    /// <summary>
    /// Rolls back a session's transaction.
    /// </summary>
    /// <param name="session">The session.</param>
    private static void RollbackSession(ISession session)
    {
        if (session == null)
            return;

        if (session.Transaction != null && session.Transaction.IsActive)
            session.Transaction.Rollback();
        session.Close();
    }
    #endregion
}

Вот статический класс, используемый для создания экземпляров ISessionFactory:

public class SessionFactoryProvider
{
    private static Configuration _configuration;

    public static Configuration Configuration
    {
        get
        {
            return _configuration;
        }
    }

    public static void Initialize()
    {
        _sessionFactory = Fluently.Configure()
                    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Database")))
                    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CustomerMap>())
                    .ExposeConfiguration(c => _configuration = c)
                    .BuildSessionFactory();
    }

    public static void Initialize(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    private static ISessionFactory _sessionFactory;

    public static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
                Initialize();
            return _sessionFactory;
        }

    }
}

Вот базовый класс репозитория:

public class BaseRepository<T>
{
    #region Properties
    /// <summary>
    /// Opens and returns a new session.
    /// </summary>
    private static ISession OpenSession
    {
        get
        {
            if (HttpContext.Current != null)
            {
                var lazySession = (Lazy<ISession>)HttpContext.Current.Items["SystemSession"];
                if (lazySession != null)
                    return lazySession.Value;
            }
            TypeContainer.Get<ILog>().Warn("Unable to find session in http context");

            throw new InvalidOperationException("The current HTTP context contains no system session.");
        }
    }

    private ISession _session;
    public ISession CurrentSession
    {
        get
        {
            if (_session == null || !_session.IsOpen || _session.Connection.State == ConnectionState.Closed)
                _session = OpenSession;
            return _session;
        }
    }
    #endregion
}

Вот несколько примеров ошибок, которые мы получаем:

CustomerID20_
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) 
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) 
at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Loader.Loader.GetKeyFromResultSet(Int32 i, IEntityPersister persister, Object id, IDataReader rs, ISessionImplementor session) 
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)

The server failed to resume the transaction. Desc:480000000f.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) 
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) 
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() 
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData() 
at System.Data.SqlClient.SqlDataReader.get_MetaData() 
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) 
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) 
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) 
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) 
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) 
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader() 
at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) 
at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)

CustomerID26_
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) 
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) 
at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Type.ManyToOneType.Hydrate(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Type.ComponentType.Hydrate(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Type.ComponentType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Loader.Loader.GetKeyFromResultSet(Int32 i, IEntityPersister persister, Object id, IDataReader rs, ISessionImplementor session) 
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)

Transaction not connected, or was disconnected
at NHibernate.Transaction.AdoTransaction.CheckNotZombied() 
at NHibernate.Transaction.AdoTransaction.Rollback() 
at ACC.Web.Modules.NHibernateSessionModule.CommitSession(ISession session) 
at ACC.Web.Modules.NHibernateSessionModule.ApplicationEndRequest(Object sender, EventArgs e) 
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

ID28_0_
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) 
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) 
at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Loader.Loader.GetKeyFromResultSet(Int32 i, IEntityPersister persister, Object id, IDataReader rs, ISessionImplementor session) 
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)

Transaction not successfully started
at NHibernate.Transaction.AdoTransaction.CheckBegun() 
at NHibernate.Transaction.AdoTransaction.Rollback() 
at ACC.Web.Modules.NHibernateSessionModule.CommitSession(ISession session) 
at ACC.Web.Modules.NHibernateSessionModule.ApplicationEndRequest(Object sender, EventArgs e) 
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

col_0_0_
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) 
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) 
at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name) 
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner) 
at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.GetResultColumnOrRow(Object[] row, IResultTransformer resultTransformer, IDataReader rs, ISessionImplementor session) 
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)

The server failed to resume the transaction. Desc:8040000001d.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) 
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) 
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() 
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 
at System.Data.SqlClient.SqlDataReader.ConsumeMetaData() 
at System.Data.SqlClient.SqlDataReader.get_MetaData() 
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) 
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) 
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) 
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) 
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) 
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader() 
at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) 
at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) 
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) 

Многие из вышеперечисленных ошибок имеют внутренние сообщения об исключениях, например:

System.InvalidOperationException: Invalid attempt to call Read when reader is closed.
System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
System.Data.SqlClient.SqlException: New request is not allowed to start because it should come with valid transaction descriptor.
System.IndexOutOfRangeException: ID28_0_
NHibernate.AssertionFailure: possible non-threadsafe access to the session
System.InvalidOperationException: ExecuteReader requires an open and available Connection. The connection's current state is open.

Многие из этих ошибок выглядятхотя сеанс открывается, доступен или закрывается в нескольких потоках, но мы не порождаем другие потоки, которые обращаются к базе данных в любом месте приложения.

Просмотр журналов NHibernate Profiler показывает нам, что транзакцииоткрываются и фиксируются / откатываются в каждом случае.

Мы уже некоторое время пытаемся решить эту проблему, и у нас закончились идеи.Кто-нибудь сталкивался с этой проблемой раньше?Есть идеи?

Спасибо!
Крис

1 Ответ

0 голосов
/ 24 января 2012

Мы не используем NHibernate, поэтому я не уверен, насколько это применимо, но мы ежедневно получали тонны ошибок GetOrdinal.Устройства чтения данных и таблицы данных возвращались с неожиданными данными (т. Е. Из совершенно другого запроса, чем мы ожидали).Недавно я обнаружил аргумент строки подключения «Enlist», и после добавления «Enlist = False» во все строки подключения мы не выдавали ни одной ошибки GetOrdinal.

Я полагаю, что это изменение, скорее всего, исправит многоо подобных проблемах люди сообщали здесь и в других местах.

Майк

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...