Am I using nHIbernate's ITransaction properly here?

I'm just learning nHibernate and have come across what probably is a simple issue to resolve.

Right so I've figured out so far that you can't/shouldn;t nest nHibernate Transactions within each other; in my case I figured this out when scope went to another routine and I started a new Transaction.

So should I be doing the following?

using (ITransaction transaction = session.BeginTransaction())
{
    NHibernateMembership mQuery =
        session.QueryOver<NHibernateMembership>()
            .Where(x => x.Username == username)
            .And(x => x.ApplicationName == ApplicationName)
            .SingleOrDefault();

    if (mQuery != null)
    {
        mQuery.PasswordQuestion = newPwdQuestion;
        mQuery.PasswordAnswer = EncodePassword(newPwdAnswer);
        session.Update(mQuery);

        transaction.Commit();
        passwordQuestionUpdated = true;
    }
}

// Assume this is in another routine elsewhere but being
// called right after the first in the same request
using (ITransaction transaction = session.BeginTransaction())
{
    NHibernateMembership mQuery =
        session.QueryOver<NHibernateMembership>()
            .Where(x => x.Username == username)
            .And(x => x.ApplicationName == ApplicationName)
            .SingleOrDefault();

    if (mQuery != null)
    {
        mQuery.PasswordQuestion = newPwdQuestion;
        mQuery.PasswordAnswer = EncodePassword(newPwdAnswer);
        session.Update(mQuery);

        transaction.Commit();
        passwordQuestionUpdated = true;
    }
}

Note: I know they are simply a copy, i'm just demonstrating my question

First Question Is this the way it is MEANT to be done? Transaction per operation?

Second Question Do I need call transaction.Commit(); each time or only in the last set?

Third Question Is there a better way, automated or manual, to do this?

Third Question Can I use session.Transaction.IsActive to determine if the "Current Session" already is part of a transaction - so in this case I can make the "Transaction wrap" in the highest level, let's say the Web Form code, and let routines be called within it and then commit all work at the end. Is this a flawed method?

I really want to hammer this down so I start as I mean to go on; I don;t want to find 1000s of lines of code in that I need to change it all.

Thanks in advance.

EDIT:

Right so I wrote some code to explain my issue exactly.

private void CallingRoutine()
{
    using(ISession session = Helper.GetCurrentSession)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            //  RUN nHIbernate QUERY to get an OBJECT-1

            //  DO WORK on OBJECT

            //  Need to CALL an EXTERNAL ROUTINE to finish work
            ExternalRoutine();

            //  DO WORK on OBJECT-1 again

            //  *** At this point ADO exception triggers
        }
    }
}

private bool ExternalRoutine()
{
    using(ISession session = Helper.GetCurrentSession)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {
            //  RUN nHIbernate QUERY to get an OBJECT-2

            //  DO WORK on OBJECT

            //  Determine result
            if(Data)
            {
                return true;
            }

            return false;
        }
    }
}

Hopefully this demonstrates the issue. This is how I understood to write the Transactions but notice how the ADO exception occurs. I'm obviously doing something wrong. How am I meant to write these routines?

Take for example if I was to write a helper object for some provider and within each routine exposed there is a nHibernate query run - how wold I write those routines, in regards to Transactions, assuming I knew nothing about the calling function and data - my job is to work with nHibernate effectively and efficiently and return a result.

This is what I assumed by writing the transaction how I did in ExternalRoutine() - to assume that this is the only use of nHibernate and to explicitly make the Transaction.

Answers


If possible, I would suggest using System.Transactions.TransactionScope:

using(var trans = new TransactionScope()) 
using(var session = .. create your session...) {
    ... do your stuff ...
    trans.Complete();
}

The trans.Complete will cause the session to flush and commit the transaction, in addition you can have nested transactionscopes. The only "downside" to this is that it will escalate to DTC if you have multiple connections (or enlisted resources such as MSMQ), but this is not necessarily a downside unless you're using something like MySQL which doesn't play nicely with DTC.

For simple cases I would probably use a transaction scope in the BeginRequest and commit it in EndRequest if there were no errors (or use a filter if u're using ASP.NET MVC), but that really depends a lot on what you're doing - as long as your operations are short (which they should be in a web app), this should be fine. The overhead of creating a session and transaction scope is not that big that it should cause problems for accessing static resources (or resources that don't need the session), unless you're looking at a really high volume / high performance website.


Need Your Help

UIWebView webCore crashes in iOS7.1 (example: www.latimes.com)

ios objective-c ipad uiwebview ios7

I have being seeing crashes in my iOS app, when opening LA Times (www.latimes.com). The crash happens in WebCore somewhere and I have no clue where. Profiling with both "Zombie" or "leaks" do not r...

Unity3d: Accessing class from another script via GetComponent error

c# unity3d reference null

FIXED I'm trying to access a class contained in another script and i get this: "Internal_Create can only be called from the main thread". The .init function just changes some values inside the cla...