34

Can anyone give me a quick overview of using TransactionScope with NHibernate? Do I need to do anything special with the session/IEnlistmentNotification/etc. to get this to work? Are there any pitfalls that I should worry about? For example, can I replace all of my hibernate transactions:

var transaction = session.BeginTransaction();
try
{
    // code
    transaction.Commit();
}
catch (Exception)
{
    transaction.Rollback();
}

with this?:

using (var scope = new TransactionScope())
{
    // code
    scope.Complete();
}

6 Answers 6

19

I have been using nHibernate 2.1 for awhile, and after a few production issues and trying quite a few variations, we have settled on the following method, as per Avoiding Leaking Connections With NHibernate And TransactionScope:

        using (var scope = new TransactionScope(TransactionScopeOption.Required))
        {
            using (var session = sessionFactory.OpenSession())
            using (var transaction = session.BeginTransaction())
            {
                // do what you need to do with the session
                transaction.Commit();
            }
            scope.Complete();
        }

As we are using MSMQ and WCF so we had to use the ambient transaction.

We found that not using session.BeginTransaction() caused a connection leak. We also found that re-using a session after committing a transaction caused a race condition (nHibernate is not thread safe and DTSC Commits/Rollbacks occur on a background thread).

Sign up to request clarification or add additional context in comments.

12 Comments

This doesn't appear to work, the internal transaction is commited before the parent scope is completed, this means id a failure on the parent scope occurs the transaction isnt rolled back
Are you sure? Have you tested it? From memory the internal transaction is not 'independent' from the parent scope. The transaction does not really commit until scope.Complete().
Hey, thanks for the reply, I understand this post is pretty old. I would expect this to act as you have said however it doesn't seem to be the case. We have tested it, take a look at the tests i've done here: gist.github.com/2759471, (sorry didn't have time to simplify the sessionFactory stuff so its a bit long-winded)
At a glance, why is line 165 Assert 0 when line 162 has saved 1. Why is line 162 even there?
I don't work on that code anymore, and my memory has faded. You error does look familiar. What I do remember is because the transaction commits on a background thread, by the time it does commit the main thread has reopened the session. The background thread then cleans up the session, and I think it killed the main thread with an error like yours, or a invalid transaction state error. This question may be related.
|
7

I've testing this out using varying vendors and it just works. If you don't have "scope.Complete()" then the transaction will roll back. You may need to have MSDTC running the machine(s) involved if there is more than one durable resource. In that case MSDTC will automatically detect the ambient ADO.NET transactions and manage the whole thing.

2 Comments

Wrong; by default TransactionScope uses the Lightweight Transaction Manager which Monitors promotion. From MSDN: As long as at most a single durable resource manager is involved, there is nothing wrong with letting the underlying resource (such as Microsoft SQL Server 2005) manage the transaction. In such case, the LTM does not need to actually manage the transaction at all—its role should be reduced to monitoring the transaction for a need for promotion.
Besides this, SqlConnection-s (if that's your DB), are 1) pooled behind the scenes, and 2) you can open more than one from a single thread without promoting the transaction as long as they don't share the transaction. To promote the transaction means to start using MSDTC.
6

The above works OK provided you are using a connection provider that supports the use of a Light-weight Transaction Manager, such as SQL Server 2005/2008.

If you are using SQL Server 7/2000 then all of your transactions will become Distributed Transactions even if you only hit one database/resource. This is probably not what you would want in most cases, and will be expensive performance wise.

So checkout if your connection provider and database server combination are suitable for use with TransactionScope.

Comments

6

I believe you can replace NHibernate transactions with this as long as you respect some constraints as some have already said:

  • Use a reasonably recent NHibernate version (>=3.1).
  • The underlying ADO.NET data provider must support TransactionScope (ok for SQL-Server, Oracle >= 10 with ODP.NET).
  • Create your NH session within the TransactionScope to ensure it gets enlisted.
  • Handle NHibernate flushing manually. See also here and here.
  • Prepare for distributed transactions. If you create multiple sessions in one scope, the initially local transaction might get promoted to a distributed transaction. I saw this happen even with same connection strings on an Oracle 11.2 DB using ODAC 11.2.0.3.20. On SQL-Server 2008 R2 it did not promote. (BTW, one can see this by watching Transaction.Current.TransactionInformation.DistributedIdentifier which is null for local transactions.) While distributed transactions have some advantages, they are more expensive and it is some extra pain setting them up. (See here how to do this for Oracle). To be sure promotion never happens in Oracle, set "Promotable Transaction=local" in your connection string. This creates an exception if some code tries to do so.

I hope that's all ;-)

PS: I added the Oracle details because they where my concern and others might benefit.

Comments

1

Also, if you are using TransactionScope, upgrade to NHibernate 2.1. It is only with 2.1 that NH really gotten good integration with TransactionScope.

1 Comment

@Rohit - can you provide any more detail with your answer?
1

According to Fabio Maulo in comments related to NH-2107:

You can use TransactionScope and you should continue using NH's transaction too. Where you have read that the Usage of TransactionScope mean the avoid of usage of NH's transaction ?

I would have assumed that explicit usage of NHibernate's transactions not necessary, but aparently that's best practice

4 Comments

I'm still looking for an authoritative answer to that question. In NH 3.0., explicit usage does not seem necessary - except perhaps to make auto-flushing work correctly.
It also causes issues with SQL Compact (attempts to create a nested transaction, which seems like a bug to me): stackoverflow.com/questions/8127735/…
I update here Andrew's link where this discussion took place: nhibernate.jira.com/browse/NH-2107
Don't think this is still the case (as of 2019-Apr-4). Current docs read: Instead of using NHibernate ITransaction, TransactionScope can be used. Please do not use both simultaneously. Reference: nhibernate.info/doc/nhibernate-reference/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.