views:

63

answers:

2

I'm using NHibernate to communicate with the database in my C# .NET project. When communicating with the database - do I always have to commit the transaction? What does this actually do when doing reads? I find myself forgetting to commit occasionally when doing reads, but everything seems to work fine.

using (var tx = Session.BeginTransaction())
{
    var fromDb = Session.Get<User>(user.Id);
    Assert.AreEqual(user.Id, fromDb.Id);
    tx.Commit(); // <-- Necessary?? 
}
+1  A: 

It is a good idea to commit or rollback a transaction. From MSDN:

BEGIN TRANSACTION starts a local transaction for the connection issuing the statement. Depending on the current transaction isolation level settings, many resources acquired to support the Transact-SQL statements issued by the connection are locked by the transaction until it is completed with either a COMMIT TRANSACTION or ROLLBACK TRANSACTION statement. Transactions left outstanding for long periods of time can prevent other users from accessing these locked resources, and also can prevent log truncation.

Darin Dimitrov
So I have to commit() or rollback() every transaction even though I have the using-clause which will dispose it?
stiank81
+5  A: 

Why do you start a transaction if you only read during that transaction ? That is completely not-necessary.

Although, it is true that, if you've set the connection-release mode to 'after_transaction', the connection will only be closed once the transaction has been committed or rollbacked. So in these cases, it can indeed by handy to start a transaction if you want to perform multiple read-actions.

In fact, what I mostly do, is this:

Person p = null;
using( ISession s = sf.OpenSession())
{
    With.Transaction (s, () => p = s.Get (1));
}

for instance.

Where 'With.Transaction' is a utility method which starts a transaction, executes the passed delegate (action), and then commit or roll backs the transaction.

It looks very much like this:

public static class With
{
    public static void Transaction( ISession s, Action proc )
    {
        using( ITransaction thx = s.BeginTransaction () )
        {
            try
            {
                proc();
                thx.Commit();
            }
            catch
            {
                 thx.Rollback();
                 throw;
            }
        }
    }
}

But, my implementation is still slightly different, since I do not use the NHibernate's ISession directly. Instead, I've created a wrapper around the ISession, and in that wrapper I also keep track of the current Transaction (if there is one) for that session. By doing so, in the With.Transaction method, I can check whether my session already has an active transaction or not, and then, I only start a transaction if there is no transaction already active.

(Credits go to Ayende for the With.Transaction idea).

Frederik Gheysels
Oh, I guess the real question is "Do I need a transaction for doing reads?" - And it sounds like your answer is "No."?
stiank81
Thanks for the sample code - looks good! So you actually have to use a transaction? I tried removing the transaction in my sample, but then it doesn't find the object on read.
stiank81
Hmm.. Neither me nor ReSharper can find the With keyword. Would you mind giving me a hint on where to find this?
stiank81
Sorry, I forgot to mention. It is a custom made class.
Frederik Gheysels
Ah, that explains it. So - I do actually need a transaction then? Also for read? And I do need to either commit or rollback each transaction to make sure all resources are freed? Even though I have a using, which will dispose the transaction? Correct?
stiank81
The using will dispose the transaction, but i believe the Commit and Rollback will do that as well, although I'm not sure about it.Using a transaction for reading can improve performance, when you have multiple 'reads' after each other. When using a transaction explicitly, it will make sure that the connection remains open for the lifetime of the transaction.When performing multiple reads after each other without a transaction, NH will close the connection after each read, and re-open it before the next read when connection-release mode is set to 'after_transaction' (which is the default).
Frederik Gheysels
I.e. for single reads it is not necessary to use transactions?
stiank81
No, but keep in mind that a single read can result in multiple reads ... :)I mean: if you retrieve an instance that has an association to another class for instance.
Frederik Gheysels
Aha. Good point! Well - thx a lot for you attention!
stiank81
does this mean that the proc() shouldn't access the transaction? For instance, if some validation failed you might want to rollback. I guess you could simply throw an exception to trigger the rollback. Or am I missing something?
dotjoe
The logic in the proc() delegate has indeed no access to the transaction.Indeed, you could throw an exception to trigger the rollback. (Which is sufficient imho; you mostly only want to rollback on an exception).If some validation fails, you just do not even start the transaction. (At least, that's what I would do).
Frederik Gheysels
Makes sense...sometimes I need to run validation to check uniqueness or maybe run some queries to check if a delete will work. So I was doing that in the same transaction. But yea, I agree I don't start a transaction unless I'm going to perform some type of query.
dotjoe
The thing you describe can be done in a transaction, but depending on the outcome of that queries or validation, you can decide inside that transaction whether you want to perform the (for instance) delete or insert or update statement, or not ...
Frederik Gheysels
good point...I really don't need to rollback unless I've already done some damage.
dotjoe