Consecutive TransactionScopes cannot be used in same NHibernate session
Description
Environment
Attachments
- 15 Apr 2010, 02:12 AM
- 15 Apr 2010, 02:12 AM
- 15 Apr 2010, 02:12 AM
depends on
Activity
Silviu ParaginaJune 30, 2011 at 5:40 AM
The issue seems to still happen in the 3.0 version from what I have seen.
The IEnlistmentNotiifcation is also called on a different thread.
The bug is also reproducible if you dispose the session right after the transaction.
As a configuration workaround you could add "<property name="connection.release_mode">on_close</property>" in the configuration, and make sure you always dispose the session after you finish a workload.
Julian MaughanJanuary 3, 2011 at 8:12 AM
I was recently reviewing the transaction code in NH (for another issue) and noticed a potential race condition, but I didn't realise it was causing anybody any problems. I don't see why it should only affect Oracle, however.
NH uses the TransactionCompleted event, but it also provides a System.Transactions resource manager (DistributedTransactionContext) that implements IEnlistmentNotification. Maybe we can move the code in the TransactionCompleted event handler to the resource manager. Unless the IEnlistmentNotiifcation callbacks also occur on a ThreadPool thread?
MohammadMDecember 25, 2010 at 2:32 AM
I have the same problem and it was seen only on Oracle 10g. Is there any hope to get some help about problem or a patch for problem. (our company cannot change the NHibernate to 3.0.0)
Fabio MauloAugust 15, 2010 at 8:17 AM
Do you have a reason to don't use the NH's transaction inside the TransactionScope ?
NormanApril 15, 2010 at 2:12 AM
Attached unit test. Note this passes with SQL Server and fails with Oracle.
"Connection is Closed" exception is thrown when a subsequent TransactionScope is used within the same the same session. This seems to be specific to Oracle (I've tested SQL Server and there was no issue).
The problem seems to be caused because the Transaction.TransactionCompleted event is fired on a different thread when using TransactionScope.
When NHibernate handles this event it closes the database connection. However by this time another transaction and database call has already started on the main thread. NHibernate has opened the connection for the second call and expects it to remain open.
Detailed information is below.
Versions:
.NET 3.5
Oracle 10g
Timeline:
MAIN THREAD: SessionFactory.OpenSession() // without passing a connection MAIN THREAD: using (new TransactionScope()) MAIN THREAD: SomeDatabaseWork(); MAIN THREAD: // NHibernate opens connection MAIN THREAD: // NHibernate does database work MAIN THREAD: // NHibernate closes connection MAIN THREAD: // SomeDatabaseWork(); returns MAIN THREAD: TransactionScope.Complete(); MAIN THREAD: TransactionScope.Dispose(); // from using MAIN THREAD: using (new TransactionScope()) MAIN THREAD: MoreDatabaseWork(); MAIN THREAD: // NHibernate opens connection ANOTHER THREAD: Transaction.TrasactionCompleted event fires ANOTHER THREAD: Nhibernate event handler closes connection MAIN THREAD: // NHibernate does database work --> EXCEPTION HERE
Exception:
NHibernate.ADOException was unhandled by user code Message="could not execute query\r\n[ select * from blah]" Source="NHibernate" SqlString="select * from blah" StackTrace: at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) at NHibernate.Loader.Loader.List(ISessionImplementor session, QueryParameters queryParameters, ISet`1 querySpaces, IType[] resultTypes) at NHibernate.Loader.Criteria.CriteriaLoader.List(ISessionImplementor session) at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) at NHibernate.Impl.CriteriaImpl.List(IList results) at NHibernate.Impl.CriteriaImpl.List[T]() at NHibernate.Linq.Visitors.ImmediateResultsVisitor`1.GetElementList(MethodCallExpression call, Int32 count) at NHibernate.Linq.Visitors.ImmediateResultsVisitor`1.HandleFirstOrDefaultCall(MethodCallExpression call) at NHibernate.Linq.Visitors.ImmediateResultsVisitor`1.VisitMethodCall(MethodCallExpression call) at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp) at NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp) at NHibernate.Linq.Visitors.ImmediateResultsVisitor`1.GetResults(MethodCallExpression expr) at NHibernate.Linq.Visitors.RootVisitor.HandleImmediateResultsCall(MethodCallExpression call) at NHibernate.Linq.Visitors.RootVisitor.VisitMethodCall(MethodCallExpression expr) at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp) at NHibernate.Linq.Visitors.NHibernateExpressionVisitor.Visit(Expression exp) at NHibernate.Linq.Visitors.NHibernateQueryTranslator.TranslateInternal(Expression expression) at NHibernate.Linq.Visitors.NHibernateQueryTranslator.Translate(Expression expression, QueryOptions queryOptions) at NHibernate.Linq.NHibernateQueryProvider.TranslateExpression(Expression expression) at NHibernate.Linq.NHibernateQueryProvider.Execute(Expression expression) at NHibernate.Linq.QueryProvider.System.Linq.IQueryProvider.Execute[T] (Expression expression) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source) at our code at SyncInvokeGetGroupWellStates(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) InnerException: System.InvalidOperationException Message="Invalid operation. The connection is closed." Source="System.Data.OracleClient" StackTrace: at System.Data.OracleClient.OracleConnection.GetOpenInternalConnection() at System.Data.OracleClient.OracleConnection.get_ErrorHandle() at System.Data.OracleClient.OracleDataReader.FillColumnInfo() at System.Data.OracleClient.OracleDataReader..ctor(OracleCommand command, OciStatementHandle statementHandle, String statementText, CommandBehavior commandBehavior) at System.Data.OracleClient.OracleCommand.ExecuteReader(CommandBehavior behavior) at System.Data.OracleClient.OracleCommand.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) InnerException:
Possible Workarounds:
1. Provide NHibernate with a connection when opening a session; or
2. Create a TransactionScope for duration of session (not ideal)
Forum Link:
http://groups.google.com/group/nhusers/browse_thread/thread/160bad2228c15fc6?fwc=1