-I need to use the lightweight transaction manager (LTM) - no
distributed transactions
-I am connecting to a single database
-I have a nested call hierarchy and use TransactionScope to demarcate
transactions
-I need to support SQL Server 2005, 2008, 2008R2, Oracle 10G, 11G, and
SQL Compact 4.0.
Does anyone have any tips or suggestions? How are you dealing with
the various and seemingly conflicting problems/workarounds with
NHibernate?
Here are a few scenarios I have tried.
Scenario 1:
Don't use NHibernate transactions at all, just use TransactionScope.
Issue:
This works great in the normal case; however this will leak a database
connection with SQL Server 2005 when a failure occurs (NH-2107 bug.
Astonished that this is marked as 'trivial.' See:
https://nhibernate.jira.com/browse/NH-2107 and
http://davybrion.com/blog/2010/05/avoiding-leaking-connections-with-nhibernate-and-transactionscope/)
using (var outerScope = new TransactionScope())
{
// Open session on first operation
using (var session =
NHibernateDataManager._sessionFactory.OpenSession())
{
using (var innerScope = new TransactionScope())
{
session.Query<MyObject>().FirstOrDefault();
innerScope.Complete();
}
using (var innerScope = new TransactionScope())
{
session.Query<MyObject>().FirstOrDefault();
innerScope.Complete();
}
}
outerScope.Complete();
}
Scenario 2:
Create an NHibernate transaction after opening a session.
Issue:
This will fail on SQL Compact with an error that it doesn't support
nested transactions. On other databases it will fail with an error
indicating that MSDTC is not enabled.
using (var outerScope = new TransactionScope())
{
// Open session on first operation
using (var session =
NHibernateDataManager._sessionFactory.OpenSession())
{
using (var innerScope = new TransactionScope())
{
using (var txn = session.BeginTransaction())
{
session.Query<MyObject>().FirstOrDefault();
txn.Commit();
}
innerScope.Complete();
}
using (var innerScope = new TransactionScope())
{
using (var txn = session.BeginTransaction())
{
session.Query<MyObject>().FirstOrDefault();
txn.Commit();
}
innerScope.Complete();
}
}
outerScope.Complete();
}
Scenario 3:
Create a new NHibernate session for each operation
Issue:
Fails just like scenario 2. This will fail on SQL Compact with an
error that it doesn't support nested transactions. On other databases
it will fail with an error indicating that MSDTC is not enabled.
using (var outerScope = new TransactionScope())
{
// Open session on first operation
using (var session =
NHibernateDataManager._sessionFactory.OpenSession())
{
using (var innerScope = new TransactionScope())
{
using (var txn = session.BeginTransaction())
{
session.Query<MyObject>().FirstOrDefault();
txn.Commit();
}
innerScope.Complete();
}
}
using (var session =
NHibernateDataManager._sessionFactory.OpenSession())
{
using (var innerScope = new TransactionScope())
{
using (var txn = session.BeginTransaction())
{
session.Query<MyObject>().FirstOrDefault();
txn.Commit();
}
innerScope.Complete();
}
}
outerScope.Complete();
}
Nested TransactionScope objects do not necessarily mean nested
transactions, just that a particular block of code should participate
in a transaction, either by creating a new ambient transaction, or
using the existing one (http://msdn.microsoft.com/en-us/library/
system.transactions.transactionscope.aspx). From what I understood,
NH 3.x supports TransactionScope. Ideally I would just use
TransactionScope, but NHibernate will leak connections to SQL Server
2005 on a rollback (http://davybrion.com/blog/2010/05/avoiding-leaking-
connections-with-nhibernate-and-transactionscope/), so I need to also
manage an NHibernate transaction.
I need to be able to manage transactions outside of the actual
database access code so that the outermost calling code actually
demarcates the transaction boundary. I am using WCF so ideally I
would just annotate my service operations with
TransactionScopeRequired and TransactionAutoComplete attributes. If
you are suggesting I forego TransactionScope altogether, I would need
to implement my own equivalent attribute that manages both an
NHibernate ISession as well as an NHibernate ITransaction. That just
seems like overkill..