I'am trying to find out the best practices for using
System.Transactions.TransactionScope with NHibernate.
I have created some unit tests to find out what works and what doesn't
work. I am stuck with a couple of questions:
First, when I execute the following code (sorry for dutch naming):
[TestMethod]
public void CanCommitNestedTransactionWhenOuterTransactionIsAborted()
{
long medewerkerId1 = 2;
long medewerkerId2 = 3;
string voornaam1;
string voornaam2;
// NHibernate Session will be openend on first call to repository
IMedewerkerRepository repository = this.CreateRepository();
using (TS.TransactionScope scope1 = new TS.TransactionScope()) {
// Update m1 in scope1.
Medewerker m1 = repository.Get(medewerkerId1);
voornaam1 = m1.Voornaam;
m1.Voornaam = DateTime.Now.ToString();
repository.Update(m1);
// Update m2 in nested scope2 -> RequiresNew
using (TS.TransactionScope scope2 = new TS.TransactionScope
(TransactionScopeOption.RequiresNew)) {
Medewerker m2 = repository.Get(medewerkerId2);
voornaam2 = m2.Voornaam;
m2.Voornaam = DateTime.Now.ToString();
repository.Update(m2);
scope2.Complete(); // scope 2 is completed
}
//NB: scope1 is not completed
}
Medewerker mCheck1 = repository.Get(medewerkerId1);
Medewerker mCheck2 = repository.Get(medewerkerId2);
Assert.AreEqual(voornaam1, mCheck1.Voornaam);
Assert.AreNotEqual(voornaam2, mCheck2.Voornaam);
// TestCleanup will dispose session
}
So scope1 should not be commited and scope2 should (in my opinion) be
committed because it is a seperate transaction (RequiresNew). To my
supprise both scopes are not committed, why, isn't RequiresNew
supported by NHibernate?
Second question:
Consider the following test:
[TestMethod]
public void CanRollbackTransaction() {
long medewerkerId = 2;
string voornaam;
// NHibernate Session will be openend on first call to repository
IMedewerkerRepository repository = this.CreateRepository();
using (TS.TransactionScope scope = new TS.TransactionScope()) {
Medewerker m = repository.Get(medewerkerId);
voornaam = m.Voornaam;
m.Voornaam = DateTime.Now.ToString();
repository.Update(m);
//NB: scope is not completed
}
Medewerker mCheck = repository.Get(medewerkerId);
Assert.AreEqual(voornaam, mCheck.Voornaam);
// TestCleanup will dispose session
}
This test does not pass although scope is rollbacked in the database.
It looks like the session cache does not get updated (no call to db in
NHProf). The second "Get" gets it's value from the cache and that
still is the updated (but rollbacked) data. When I create my own
wrapper arround TransactionScope and call ISession.Clear() on the
current NHibernate session the test passes. But is this the way to go?
I am a bit stuck on how to use System.Transactions.TransactionScope
with NHibernate. I need to use the TransactionScope class because of
other db activity outside NHibernate in the same transaction.
Kind regards,
Henk Huisman
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
A little more background:
I am using the "OpenSessionInView" method for my session handling in
my asp.net MVC application.
However, the session is opened when it is first needed and is disposed
in the Application_EndRequest method.
I am not opening any NHibernate transactions though.
Do you have any ideas on how to make this work?
Henk
On 14 jan, 22:29, Fabio Maulo <fabioma...@gmail.com> wrote:
> mmm.... where is the Begin and Commit of NH's transaction ?
>
> 2010/1/14 HH <hwhuis...@gmail.com>
> > nhusers+u...@googlegroups.com<nhusers%2Bunsu...@googlegroups.com>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/nhusers?hl=en.
>
> --
> Fabio Maulo- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
Do you have any ideas on how to make this work?
OnActionExecuting:
1) Open Session (if not open)
2) CreateTransactionScope
3) Start NHibernate Transaction
OnActionExecuted:
if ([errorsOccured])
Rollback NHibernate transaction
else
Commit NHiberate transaction
TransactionScope.Complete()
Session will be closed on end request.
This makes me have to manage both the TransactionScope and NHibernate
transaction.
Why?
Henk
On 14 jan, 22:49, Fabio Maulo <fabioma...@gmail.com> wrote:
> 2010/1/14 HH <hwhuis...@gmail.com>
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
On Jan 14, 6:35 pm, Fabio Maulo <fabioma...@gmail.com> wrote:
> you can commit the NH's transaction and leave the real action to TS
> (Abort/Complete).
> Why ?...
> Well... you should manage the NH's always and occasionally the NH's will
> work in an Ambient-Transaction.
> The code where the NH's transaction is managed shouldn't not be aware about
> if it is working in Ambient-Transaction or not.
> For those using AOP this is pretty normal.
>
> btw, have you tried using the NH's transaction in your tests ?
>
> 2010/1/14 HH <hwhuis...@gmail.com>
> > nhusers+u...@googlegroups.com<nhusers%2Bunsu...@googlegroups.com>
I am still a bit confused. Now I am using the following test code:
[TestMethod]
public void CanRollbackTransaction() {
long medewerkerId = 2;
string voornaam;
using (var session = NHibernateSession.Current) // session will be
opened
using (NHibernate.ITransaction trans = session.BeginTransaction()) {
using (var ts = new TransactionScope()) {
Medewerker m = session.Get<Medewerker>(medewerkerId);
voornaam = m.Voornaam;
m.Voornaam = DateTime.Now.ToString();
session.Update(m);
//NB: scope is not completed
}
trans.Commit(); // This throws NHibernate.TransactionException:
Transaction not connected, or was disconnected.
}
using (var session = NHibernateSession.Current) {
Medewerker mCheck = session.Get<Medewerker>(medewerkerId);
Assert.AreEqual(voornaam, mCheck.Voornaam);
}
}
The above code does not succeed when commiting the NHibernate
Transaction I receive the following (obvious) error:
NHibernate.TransactionException: Transaction not connected, or was
disconnected.
Next test looked like this:
[TestMethod]
public void CanRollbackTransaction() {
long medewerkerId = 2;
string voornaam;
string voornaamCheck;
using (var session = NHibernateSession.Current) {
using (var ts = new TransactionScope()) {
using (NHibernate.ITransaction trans = session.BeginTransaction())
{
Medewerker m = session.Get<Medewerker>(medewerkerId);
voornaam = m.Voornaam;
m.Voornaam = DateTime.Now.ToString();
session.Update(m);
trans.Commit();
}
//NB: scope is not completed
}
using (NHibernate.ITransaction trans = session.BeginTransaction()) {
Medewerker mCheck = session.Get<Medewerker>(medewerkerId);
voornaamCheck = mCheck.Voornaam;
trans.Commit();
}
Assert.AreEqual(voornaam, voornaamCheck);
}
}
So NHTransaction nested in TS and check on equalness in same session
(not same transaction). Again no succes. The Session.Get gives me the
"wrong" value.
My main issue is that the NHibernate action are only part of the
solution, so when writing a "Transaction" ActionFilterAttribute for
the controllers of my MVC classes I want to make sure that all actions
in the controller succeed or do not succeed. So I must use TS as the
main transaction handler and I want NHibernate transactions to be part
of the "main transaction" that is the TS transaction.
Any suggestions?
Henk
On 15 jan, 00:35, Fabio Maulo <fabioma...@gmail.com> wrote:
> you can commit the NH's transaction and leave the real action to TS
> (Abort/Complete).
> Why ?...
> Well... you should manage the NH's always and occasionally the NH's will
> work in an Ambient-Transaction.
> The code where the NH's transaction is managed shouldn't not be aware about
> if it is working in Ambient-Transaction or not.
> For those using AOP this is pretty normal.
>
> btw, have you tried using the NH's transaction in your tests ?
>
> 2010/1/14 HH <hwhuis...@gmail.com>
> > nhusers+u...@googlegroups.com<nhusers%2Bunsu...@googlegroups.com>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/nhusers?hl=en.
>
> --
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.
-- statement #1
enlisted session in distributed transaction with isolation level:
Serializable
-- statement #2
begin transaction with isolation level: Unspecified
-- statement #3
SELECT .......
-- statement #4
UPDATE ......
-- statement #5
commit transaction
-- statement #6
rollback transaction
-- statement #7
begin transaction with isolation level: Unspecified
-- statement #8
commit transaction
No select between statment #7 and #8.
@Jason: what if I opened a session before TS? Will disconnecting en
reconnecting the session register it with the TS?
On 15 jan, 14:50, Fabio Maulo <fabioma...@gmail.com> wrote:
> Did you see SQL ?
> Did you see a SELECT in the second session.Get ?
>
> 2010/1/15 HH <hwhuis...@gmail.com>
> > <nhusers%2Bunsu...@googlegroups.com<nhusers%252Bunsubscribe@googlegroups.com>
-- statement #7
begin transaction with isolation level: Unspecified
-- statement #8
commit transaction
No select between statment #7 and #8.
I don't want to sound rude, but I know that the get is comming from
the cache. I am not sure what you are trying to say, but isn't it
strange that NHibernate returns data from a cache based on a
transaction which was rollbacked? Or should I manage the NHibernate
cache by calling Clear() on the ISession?
I find it very hard to find out to which extend NHibernate supports
working with TS and where I should manage things myself.
Henk
On 15 jan, 18:35, Fabio Maulo <fabioma...@gmail.com> wrote:
> 2010/1/15 HH <hwhuis...@gmail.com>
var value = "new text";
using(var ts = new TransactionScope())
using(var session = factory.OpenSession())
{
session.Get<Entity>(id).Property = value;
//rolling back
}
using(var ts = new TransactionScope())
using(var session = factory.OpenSession())
{
var property = session.Get<Entity>(id).Property;
Assert.That(property).Does.Not.Equal(value);
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.