-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Using TransactionScope with NHibernate Session
PostPosted: Sun Apr 09, 2006 12:43 am 
Regular
Regular

Joined: Sun Sep 25, 2005 11:35 pm
Posts: 57
What is the correct way of using TransactionScope with NHibernate Session? I understand that this is how to use TransactionScope:
Code:

            using (TransactionScope transactionScope = new TransactionScope())
            {
                ---- Use NHibernate session here ----
                transactionScope.Complete();
            }


I am not sure where I should open and close the NHibernate session and how to make sure that the session is closed if an exception occurs. I generally use the following code with NHibernate transactions:

Code:
            SessionManagerFactory.SessionManager.HandleSessionStart();
            SessionManagerFactory.SessionManager.BeginTransaction();
            try
            {
                ---- Use NHibernate session here ----
                SessionManagerFactory.SessionManager.CommitTransaction();
            }
            catch (Exception ex)
            {
                SessionManagerFactory.SessionManager.RollbackTransaction();
                log.Error(ex);
                throw;
            }
            finally
            {
                SessionManagerFactory.SessionManager.HandleSessionEnd();
            }


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 10, 2006 5:32 am 
Newbie

Joined: Mon Apr 10, 2006 5:21 am
Posts: 2
Location: Amsterdam
As far as I know, Hibernate does not implement the required IEnlistmentNotification interface to be able to operate within System.Transactions.TransactionScope.

I use my own DbSession helper object when dealing with Hibernate and I adapted it to be able to use System.Transactions:

using( TransactionScope scope = new TransactionScope())
{
Person p = DbSession.Current.Find<Person>(20);

p.Name = "Paul";

DbSession.Current.Save(p);

scope.Commit();
}


Top
 Profile  
 
 Post subject: TransactionScope code for NHibernate
PostPosted: Fri Apr 21, 2006 10:42 pm 
Newbie

Joined: Thu Nov 17, 2005 6:37 pm
Posts: 13
Hi,
We've been working on a solution for integrating NH easily into TransactionScope code, particularly as we have a solution which uses the ASP.Net 2.0 sql membership api, which has a different connection string and stores data in a separate db from the NH powered application.

We needed to have a single TransactionScope which workes with NH save/updates, and we have done it!

Here is a static helper method that does the job.

public static TransactionScope GetNewScope(TransactionScopeOption scopeOption)
{
Trace.Write("TransactionScopeWorker: Getting New Scope");
bool newTransaction = System.Transactions.Transaction.Current == null;
if (newTransaction)
SessionFactory.DisconnectCurrentSession();
TransactionScope scope = new TransactionScope(scopeOption);
if (newTransaction)
SessionFactory.ReconnectCurrentSession();
return scope;
}

So a worker class like TransactionScopeWorker could help manage the whole thing as followed:

using (TransactionScope scope = TransactionScopeWorker.GetNewScope(TransactionScopeOption.Required)
{
try
{
// TRANSACTION CODE HERE

}
catch (Exception ex)
{
// HANDLE EXCEPTION HERE
return;
}
scope.Complete;
}

The SessionFactory call is NHibernate related (see code at bottom below). What we are doing is keeping the current NH session, but removing it's link with the current connection. Then we are starting up a TransactionScope and reconnecting the NH session, which starts up a new connection (or more likely retrieves it from connection pool) which gets enlisted in the TransactionScope.

Note that we only disconnect/reconnect if it's a new transaction, as you may have nested transaction scopes and therefore the disconnect/reconnect step is not necessary.

Why? Well, if we didn't do this, any existing NH session would not participate in the transaction, because it's connection would have existed before the TransactionScope was created and it doesn't automatically become enlisted with TransactionScope. Somehow (and i don't know if this is possible) NH needs to be updated so that the existing session's underlying connection gets automatically linked to the transaction when TransactionScope is created.

This method therefore ensures two things. Firstly, that all database calls (including NH calls, but also calls to other databases) are committed or rolled back as a single unit of work. Secondly, that any objects created and used before the transaction started are still available, including lazy loading etc. during the transaction. This second benefit would not be possible if we closed the session, which is how things were working until yesterday.

In addition, it is crucial that Save Update Delete NH methods include a call to Session.Flush(), before TransactionScope.Complete is called. We use a session per http request pattern, so if we didn't flush the session the sql would be executed at the end of the request, not inside the transaction scope!

In summary, do the following:

1. Disconnect NH Session - Start Transaction Scope - Reconnect NH
Session
2. Perform DB calls. Ensure all NH save/update/delete calls are followed by Session.Flush()

Here is the additional Disconnect and Reconnect methods we use, as we have found it necessary to check various properties of the NH Session to ensure it can be disconnected/reconnected/

/// <summary>
/// disconnects the current session (if not in active transaction)
/// </summary>
public static void DisconnectCurrentSession()
{
ISession session = GetCurrentSession(); // our method
if (session == null) return;
if (!session.IsConnected) return;
if (session.Transaction == null)
session.Disconnect();
else
{
if (session.Transaction.WasCommitted || session.Transaction.WasRolledBack)
session.Disconnect();
}
}

/// <summary>
/// disconnects the current session (if not already connected)
/// </summary>
public static void ReconnectCurrentSession()
{
ISession session = GetCurrentSession(); // our method
if (session == null)
return;
if (!session.IsConnected)
session.Reconnect();
}


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 07, 2006 11:31 am 
Newbie

Joined: Thu Sep 07, 2006 11:15 am
Posts: 2
Hi all,

Can anyone post if this approach has any problems? I have implemented it with various NHibernate sessions (to different DBs) inside the transactionScope, and it works!

BUT i have some questions:

1) Why you have to flush after each operation? I've tried without flushing, and it works too.

2) If I try to control transactions with nHibernate also, it doesn't work (because the sessions doesn't disconnect, see DisconnectCurrentSession - DisconnectCurrentSessions in my case) has anyone had the same problem? I must rely the transactions only on System.Transactions??

Here's my code:

Code:
       
using (TransactionScope scope = TransactionScopeWorker.GetNewScope(TransactionScopeOption.Required))
        {
            try
            {
                IDAOFactory daoFac = new TFDaoFactory();
                IClienteDao cDao = daoFac.GetClienteDao();
                IContactoDao conDao = daoFac.GetContactoDao();

                ContactoDN con = new ContactoDN("Manolo2", "El del Bombo2");
                conDao.Save(con);

                ClienteParticular cp = new ClienteParticular("10.101.011-A", "Paquita", "Rico");
                cDao.Save(cp);

                // Commenting this line doesn't matter to me! It works the same way! Why?
                //NHibernateMultiSessionManager.Instance.FlushAllSessions();

                //Create an error (division by cero)
                //int i = 1;
                //i -= 1;
                //double d = 2 / i;

                scope.Complete();
            }
            catch (Exception)
            {
                throw;
            }
        }



TransactionScopeWorker is very similar to the one specified here... the differences in the whole code are just to mantain different sessions pointing to different DBs.... If you have any doubts, please tell me and i'll post more code...

THX in advance....

Mahouri.


Top
 Profile  
 
 Post subject: why session.flush is required
PostPosted: Thu Sep 07, 2006 7:37 pm 
Newbie

Joined: Thu Nov 17, 2005 6:37 pm
Posts: 13
Hi,
Thanks for posting back, good you got it working. First of all, this code assumes you are using .net 2.0 and are happy to use System.Transactions for all your transactional management.

The Session.Flush() call is required to ensure that the sql is executed withing the context of the transaction scope at the point of actually calling Save(), Delete() etc. This ensures all sql operations whether via NH or via say the ASP.Net membership api are executed in a linear fashion within the context of the transaction scope.

I had problems if i did not call sessinon.flush in that i ended up getting failures within say a call to another db which didn't rollback the NH work because the session was being flushed outside of the transaction.

We now use this class as part of a compiled utilities dll for NH with great success for all our .net 2.0 apps


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 08, 2006 4:32 am 
Newbie

Joined: Thu Sep 07, 2006 11:15 am
Posts: 2
BAD NEWS!

It works perfectly if I use SQL Server in all connections. I've tried this morning with a connection to SQLServer, and another to MySql, and it doesn't work.... it does not rollback, a pitty.

So I think i'll try with COM+ distributed transactions to see what happends...

Mahouri.


Top
 Profile  
 
 Post subject: transactionscope with MySQl
PostPosted: Fri Sep 08, 2006 10:42 am 
Newbie

Joined: Thu Nov 17, 2005 6:37 pm
Posts: 13
Yep, i'm pretty sure it won't work with non MS databases.


Top
 Profile  
 
 Post subject: Re: transactionscope with MySQl
PostPosted: Fri Sep 08, 2006 1:06 pm 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
kpatton wrote:
Yep, i'm pretty sure it won't work with non MS databases.


It depends on Database. Oracle does support. But with previous .net provider version, the TransactionScope needed to be created with EnterpriseServicesInteropOption.Full specified.

Edit: if database supports COM+ distributed transactions, then the TransactionScope can be made to work also. Otherwise, propably not.

Gert

_________________
If a reply helps You, rate it!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.