我看到一些奇怪的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的日志告诉我们在每种情况下都会打开并提交/回滚事务。
我们一直试图解决这个问题已经有一段时间了,并且已经没有想法了。有没有人遇到过这个问题?有什么想法吗?
谢谢!
克里斯
答案 0 :(得分:0)
我们没有使用NHibernate,所以我不确定这是多么适用,但我们每天都会遇到大量的GetOrdinal错误。数据引擎和数据表带有意外数据(即来自完全不同的查询,而不是我们预期的数据)。我最近发现了“Enlist”连接字符串参数,在向所有连接字符串添加“Enlist = False”之后,我们没有抛出一个GetOrdinal错误。
我相信这一改变可能会解决人们在此处和其他地方报道的类似问题。
麦克