奇怪的NHibernate错误

时间:2011-11-07 20:42:41

标签: nhibernate session transactions

我看到一些奇怪的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 :(得分:0)

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

我相信这一改变可能会解决人们在此处和其他地方报道的类似问题。

麦克