【问题标题】:Strange NHibernate errors奇怪的 NHibernate 错误
【发布时间】:2011-11-07 20:42:41
【问题描述】:

我看到一些奇怪的 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 的日志告诉我们,在每种情况下,事务都被打开并提交/回滚。

一段时间以来,我们一直在尝试解决这个问题,但已经没有什么想法了。有没有人遇到过这个问题?有什么想法吗?

谢谢!
克里斯

【问题讨论】:

  • 您可能应该重新抛出捕获的异常,而不是尝试回滚并忽略它...
  • 嗨,克里斯,我们在 nHibernate 中看到了类似的奇怪行为。事情会间歇性地工作,然后我们会收到与您报告的类似的错误。你有没有解决过这个问题?谢谢。

标签: nhibernate session transactions


【解决方案1】:

我们没有使用 NHibernate,所以我不确定这是否适用,但我们每天都会收到大量的 GetOrdinal 错误。数据读取器和数据表返回意外数据(即来自与我们预期完全不同的查询)。我最近发现了“Enlist”连接字符串参数,在将“Enlist=False”添加到我们所有的连接字符串之后,我们没有抛出一个 GetOrdinal 错误。

我相信这一变化可能会解决人们在这里和其他地方报告的许多类似问题。

迈克

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-04
    • 2010-11-17
    • 1970-01-01
    • 2014-10-11
    • 2012-04-27
    • 2013-10-30
    • 2016-09-18
    • 2012-03-18
    相关资源
    最近更新 更多