5

The following code snippet works fine with SQL Server 2008 (SP1) but with Oracle 11g the call to session.BeginTransaction() throws an exception with the message ‘Connection is already part of a local or a distributed transaction’ (stack trace shown below). Using the '"NHibernate.Driver.OracleDataClientDriver".

Has anyone else run into this?

using (var scope = new TransactionScope())
{
   using (var session = sessionFactory.OpenSession())
   using (var transaction = session.BeginTransaction())
   {
      // do what you need to do with the session
      transaction.Commit();
    }
    scope.Complete();
}
     Exception at:    at NHibernate.Transaction.AdoTransaction.Begin(IsolationLevel isolationLevel)
           at NHibernate.Transaction.AdoTransaction.Begin()
           at NHibernate.AdoNet.ConnectionManager.BeginTransaction()
           at NHibernate.Impl.SessionImpl.BeginTransaction()
           at MetraTech.BusinessEntity.DataAccess.Persistence.StandardRepository.SaveInstances(List`1& dataObjects) in S:\MetraTech\BusinessEntity\DataAccess\Persistence\StandardRepository.cs:line 3103

        Inner error message was: Connection is already part of a local or a distributed transaction
        Inner exception at:    at Oracle.DataAccess.Client.OracleConnection.BeginTransaction(IsolationLevel isolationLevel)
           at Oracle.DataAccess.Client.OracleConnection.BeginDbTransaction(IsolationLevel isolationLevel)
           at System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()
           at NHibernate.Transaction.AdoTransaction.Begin(IsolationLevel isolationLevel)
flq
  • 22,247
  • 8
  • 55
  • 77
Sudip
  • 51
  • 1
  • 3

4 Answers4

7

The problem with using only the transaction scope is outlined here: NHibernate FlushMode Auto Not Flushing Before Find

It appears nhibernate (v3.1 with oracle dialect and 11g db w/opd.net v2.112.1.2) requires it's own transactions to avoid the flushing issue but I haven't been able to get the transaction scope to work with the nhibernate transactions.

I can't seem to get it to work :( this might be a defect in nhibernate or odp.net, not sure...

found same problem here: NHibernate 3.0: TransactionScope and Auto-Flushing

FIXED: found a solution! by putting "enlist=dynamic;" into my oracle connection string, the problem was resolved. I have been able to use both the nhibernate transaction (to fix the flush issue) and the transaction scope like so:

        ISessionFactory sessionFactory = CreateSessionFactory();

        using (TransactionScope ts = new TransactionScope())
        {
            using (ISession session = sessionFactory.OpenSession())
            using (ITransaction tx = session.BeginTransaction())
            {
                //do stuff here

                tx.Commit();

            }
            ts.Complete();
        }

I checked my log files and found this: 2011-06-27 14:03:59,852 [10] DEBUG NHibernate.Impl.AbstractSessionImpl - enlisted into DTC transaction: Serializable

before any SQL was executed on the connection. I will unit test to confirm proper execution. I'm not too sure what serializable is telling me though

Community
  • 1
  • 1
Brad
  • 705
  • 7
  • 17
2

Brads answer, using an outer TransactionScope and an inner NHibernate transaction with enlist=dynamic, doesn't seem to work properly. Ok, the data gets committed.

But if you omit the scope.Complete() or raise an exception after tx.Commit() the data still gets committed (for Oracle)! However, for some reason this works for SQL-Server.

NHibernate transactions take care of auto-flush but in the end they call the underlying ADO.NET transaction. While many sources encourage the above pattern as best practice for NHibernate to solve the auto-flush issue, sources discussing native ADO.NET say the contrary: Do NOT use TransactionScope and inner transactions together, not for Oracle and not for SQL-Server. (See this question and my answer)

My conclusion: Do not combine TransactionScope and NHibernate transactions. To use TransactionScope, skip NHibernate transactions and handle the flushing manually (see also NHibernate Flush doc).

Community
  • 1
  • 1
Piper
  • 851
  • 11
  • 14
0

From NHibernate cookbook

Remember that NHibernate requires an NHibernate transaction when interacting with the database. TransactionScope is not a substitute. As illustrated in the next image, the TransactionScope should completely surround both the session and NHibernate transaction. The call to TransactionScope.Complete() should occur after the session has been disposed. Any other order will most likely lead to nasty, production crashing bugs like connection leaks.

My opinion is also that it should work with TransactionScope along, but it does not, neither in 3.3.x.x neither in 4.0.0.400 version.

The recipe above may work, but need to test it with nested TrancactionScope, with inner TransactionScope that has a Transaction.Suppress defined (when using SQL), etc...

baHI
  • 1,510
  • 15
  • 20
0

One question, why are you doing the inner session.BeginTransaction - since 2.1 GA NHibernate will automatically enroll into TransactionScope contexts so there's no reason to do your own anymore.

Paul Hatcher
  • 7,342
  • 1
  • 54
  • 51