Full integration with Ambient Transaction/TransactionScope.

437 views
Skip to first unread message

John T

unread,
Apr 13, 2012, 6:56:41 AM4/13/12
to nhibernate-...@googlegroups.com
Hi group,

so I've discovered that NHibernate does not integrate at all well with the Ambient Transaction. In fact, when using NHibernate within a TransactionScope, one would be forgiven for thinking it doesn't integrate at all.

What should be the correct usage:

public void Foo()
{
   ISession session = null; // get session from wherever

   using (var transactionScope = new TransactionScope())
   {
     session.Save(new PersistableObject { ArbitraryProperty = "a value" });
     transactionScope.Complete();
   }
}

is completely useless. What you actually have to do is:

public void Foo()
{
   ISession session = null; // get session from wherever

   using (var transactionScope = new TransactionScope())
   using (var transaction = session.BeginTransaction())
   {
     session.Save(new PersistableObject { ArbitraryProperty = "a value" });
     transaction.Commit();
     transactionScope.Complete();
   }
}

So the fact that NHibernate has any integration with the Ambient Transaction seems completely pointless.

Now, I've looked (only cursory thus far) through the NHib src and have noted a few areas of interest wrt to integrating with the Ambient Transaction. But I want to ask if anyone has tried this already, and hit any barriers along the way?

Regards,
John.

Fabio Maulo

unread,
Apr 13, 2012, 7:04:20 PM4/13/12
to nhibernate-...@googlegroups.com
the integration is that the NH's transaction enlist itself in the TransactionScope
--
Fabio Maulo

James Kovacs

unread,
Apr 14, 2012, 2:50:51 PM4/14/12
to nhibernate-development
I'm confused by your assertion. I have used NHibernate in production
applications using TransactionScope for transaction management.
NHibernate properly enlisted in the ambient transaction. There was no
need to call session.BeginTransaction()/tx.Commit() as well as new
TransactionScope()/scope.Complete(). Just the latter was sufficient
for proper transaction semantics. Are you using NHibernate 3.2? Can
you provide a test case demonstrating the issue that you're seeing?

James

Ramon Smits

unread,
Apr 14, 2012, 4:16:26 PM4/14/12
to nhibernate-...@googlegroups.com

Make sure that your session is created AFTER the transactionscope.
--
Ramon

John T

unread,
Apr 16, 2012, 6:18:07 AM4/16/12
to nhibernate-...@googlegroups.com
This is/was our problem. We are using WCF to manage the TransactionScope, and are also using custom InstanceProviders to generate the Service's dependencies - including the NHibernate session.

What would it take to get the session to enlist in the transactionscope even if the session has been created before the transactionscope has? 

To better ask the above question is: What would stop that from happening? 

The reason I am looking into this avenue and not changing the rest of our architecture is that it still does not make sense to have to create the Session AFTER the transactionscope. If we were not using TransactionScope, it just wouldn't make sense (nor even be possible) to start a Transaction before creating the Session - so why so different for a TransactionScope?

Regards,
J.

On Saturday, April 14, 2012 9:16:26 PM UTC+1, Ramon Smits wrote:

Make sure that your session is created AFTER the transactionscope.

John T

unread,
Apr 16, 2012, 6:20:28 AM4/16/12
to nhibernate-...@googlegroups.com
Hi, The example code in my original post is a perfect example. The former doesn't persist, and the latter does.

Regards,
J.

On Saturday, April 14, 2012 7:50:51 PM UTC+1, James Kovacs wrote:
I'm confused by your assertion. I have used NHibernate in production
applications using TransactionScope for transaction management.
NHibernate properly enlisted in the ambient transaction. There was no
need to call session.BeginTransaction()/tx.Commit() as well as new
TransactionScope()/scope.Complete(). Just the latter was sufficient
for proper transaction semantics. Are you using NHibernate 3.2? Can
you provide a test case demonstrating the issue that you're seeing?

James

Diego Mijelshon

unread,
Apr 16, 2012, 8:56:32 AM4/16/12
to nhibernate-...@googlegroups.com
The former would probably persist if you flushed the session. The problem is, there's absolutely nothing telling NH to save the changes to the DB.
 
    Diego

John T

unread,
Apr 16, 2012, 9:16:08 AM4/16/12
to nhibernate-...@googlegroups.com
.. which is the crux of this problem/discussion. There should be, because it is used within a TransactionScope, and when the TransactionScope completes - so should NHibernate.


On Monday, April 16, 2012 1:56:32 PM UTC+1, Diego Mijelshon wrote:
The former would probably persist if you flushed the session. The problem is, there's absolutely nothing telling NH to save the changes to the DB.
 
    Diego


Diego Mijelshon

unread,
Apr 16, 2012, 10:00:50 AM4/16/12
to nhibernate-...@googlegroups.com
Then, how would you handle this?

    var session = GetSession();
    var foo = session.Query<Foo>(...);
    using (var transactionScope = new TransactionScope())
    {
        foo.Bar = newValue;
        transactionScope.Complete();
    }
 
The update could never happen as there's no way for NH to catch the change.
I't likely the same with other frameworks like EF; if you don't call SaveChanges (which is the method that creates the transaction, flushes and commits), completing the scope will do nothing.

    Diego

John T

unread,
Apr 16, 2012, 11:34:02 AM4/16/12
to nhibernate-...@googlegroups.com
Please re-read my examples. 

The error in your example is that "foo" is instantiated outside of the TransactionScope. In both of my examples the equivalent query occurs INSIDE the TransactionScope.

either way, given that "foo" will be an instance of an NHibernate proxy, Yes indeed: why couldn't NHibernate know it is in a TransactionScope ?! 

Regards,
J.

On Monday, April 16, 2012 3:00:50 PM UTC+1, Diego Mijelshon wrote:
Then, how would you handle this?

    var session = GetSession();
    var foo = session.Query<Foo>(...);
    using (var transactionScope = new TransactionScope())
    {
        foo.Bar = newValue;
        transactionScope.Complete();
    }
 
The update could never happen as there's no way for NH to catch the change.
I't likely the same with other frameworks like EF; if you don't call SaveChanges (which is the method that creates the transaction, flushes and commits), completing the scope will do nothing.

    Diego

Diego Mijelshon

unread,
Apr 16, 2012, 12:02:27 PM4/16/12
to nhibernate-...@googlegroups.com
Both of your examples are for inserts.
In neither case the instantiation location is important (you can try)
In neither case the object is a proxy, nor would it have to be even if it was already persistent.
And in both cases, trying to update the object afterwards with just a TransactionScope would not work.

Just accept that it's not technically feasible for NH (or any other framework) to do what you want, nor is it desirable for most people using it. Completing a scope and flushing a unit of work are different things.
 
    Diego

John T

unread,
Apr 16, 2012, 12:11:10 PM4/16/12
to nhibernate-...@googlegroups.com
That is a completely fallacious statement.

See the number of people on this issue asking for exactly the same thing:


It is ABSOLUTELY feasible. MEF does it. Even L2S does it.

Trying to update an object with just a TransactionScope is a perfectly acceptable desire - that is what the TS was designed for!

J.

On Monday, April 16, 2012 5:02:27 PM UTC+1, Diego Mijelshon wrote:
Both of your examples are for inserts.
In neither case the instantiation location is important (you can try)
In neither case the object is a proxy, nor would it have to be even if it was already persistent.
And in both cases, trying to update the object afterwards with just a TransactionScope would not work.

Just accept that it's not technically feasible for NH (or any other framework) to do what you want, nor is it desirable for most people using it. Completing a scope and flushing a unit of work are different things.
 
    Diego

John T

unread,
Apr 16, 2012, 12:12:02 PM4/16/12
to nhibernate-...@googlegroups.com
typo in previous: https://nhibernate.jira.com/browse/NH-2107


On Monday, April 16, 2012 5:02:27 PM UTC+1, Diego Mijelshon wrote:
Both of your examples are for inserts.
In neither case the instantiation location is important (you can try)
In neither case the object is a proxy, nor would it have to be even if it was already persistent.
And in both cases, trying to update the object afterwards with just a TransactionScope would not work.

Just accept that it's not technically feasible for NH (or any other framework) to do what you want, nor is it desirable for most people using it. Completing a scope and flushing a unit of work are different things.
 
    Diego

Diego Mijelshon

unread,
Apr 16, 2012, 1:04:58 PM4/16/12
to nhibernate-...@googlegroups.com
There are a few problems:
- What you posted is absolutely different from the NH-2107. There, the session is being opened inside the transactionscope, which is a point where an ambient transaction COULD be detected.
- I won't even try to compare this to the (discontinued) L2S, which doesn't use POCOs.
- What is "the number of people asking for exactly the same thing"? There are thousands of projects using NH, none of which has problems with using NH transactions alone, or combined with TransactionScope. Five users, none of which seem to have looked at the implementation and implications of what they suggest, or provided a patch, are far from being a significative sample.
 
    Diego

John T

unread,
Apr 16, 2012, 1:22:37 PM4/16/12
to nhibernate-...@googlegroups.com
And yet TransactionScope is still designed to be the one place that developers like you and I need to Complete a transaction.

Else what is the point of using it? To create a n phase commit just for fun? What is the purpose of System.Transactions.Transaction.TransactionCompleted if not to do exactly this?

As for your last somewhat irrelevant point: So what if there are only "five" people asking? So what if they aren't given patches, nor looked at the implementation in detail? What the hell do you think I'm trying to start here? I'm not demanding this be fixed for me, I'm starting a discussion on how can NH achieve this. In my very first post I have asked if anyone has hit any barriers in any previous attempts to do this before. So far, I've gathered that there is a very big barrier in the shape of a huge "holier than thou" attitude from certain devs that sit on their haunches and bleat the same tired arguments "you aren't going to need it" and "you're doing it wrong."

The very idea behind TransactionScope is so that I, nor anyone else, do not need to make any more than ONE explicit commit/rollback call in my code. And here on this list I am being told (incorrectly) that that is not the case. I'm being told that I must create a session after a transactionscope. Whilst that was/is helpful advice (it does "work") it, as is explained by one of the comments in NH-2107 quite elegantly with diagrams, breaks the model of session vs transaction. Many transaction(scope)s per session. Not one-to-one session-to-transaction(scope). Or worse, many sessions to one transaction(scope).

Regards,
J.

On Monday, April 16, 2012 6:04:58 PM UTC+1, Diego Mijelshon wrote:
There are a few problems:
- What you posted is absolutely different from the NH-2107. There, the session is being opened inside the transactionscope, which is a point where an ambient transaction COULD be detected.
- I won't even try to compare this to the (discontinued) L2S, which doesn't use POCOs.
- What is "the number of people asking for exactly the same thing"? There are thousands of projects using NH, none of which has problems with using NH transactions alone, or combined with TransactionScope. Five users, none of which seem to have looked at the implementation and implications of what they suggest, or provided a patch, are far from being a significative sample.
 
    Diego

Diego Mijelshon

unread,
Apr 16, 2012, 2:25:35 PM4/16/12
to nhibernate-...@googlegroups.com
OK, is my last post on this thread. I won't waste more time, so feel free to continue your bashing.
In Microsoft, all features start at minus 100 points, because they take effort to implement, document, and test for compatibility.
So far, you have failed to prove that your feature goes beyond a minor convenience issue, and to resolve the potential inconsistencies, much less provide any actual pointers on how such a feature might be implemented.
I don't know what you are "trying to start here", but I given examples and reasons why it would be hard (or impossible) to implement that in NH.
Regarding the model: You can have N-transactions and transaction scopes per session; you just have to create the transactions inside the scopes (remember transactionscopes are primarily wrappers for distributed transactions). If your code uses different assumptions, that's fine, but don't blame NH for that. 
You are free to use any ORM you like (I do use EF in some projects, having choices is wonderful).
 
    Diego

John T

unread,
Apr 17, 2012, 5:33:01 AM4/17/12
to nhibernate-...@googlegroups.com
You haven't given any reason why it would be hard or impossible to implement in NH.

Thank you for your input none the less.

Regards,
J.

On Monday, April 16, 2012 7:25:35 PM UTC+1, Diego Mijelshon wrote:
OK, is my last post on this thread. I won't waste more time, so feel free to continue your bashing.
In Microsoft, all features start at minus 100 points, because they take effort to implement, document, and test for compatibility.
So far, you have failed to prove that your feature goes beyond a minor convenience issue, and to resolve the potential inconsistencies, much less provide any actual pointers on how such a feature might be implemented.
I don't know what you are "trying to start here", but I given examples and reasons why it would be hard (or impossible) to implement that in NH.
Regarding the model: You can have N-transactions and transaction scopes per session; you just have to create the transactions inside the scopes (remember transactionscopes are primarily wrappers for distributed transactions). If your code uses different assumptions, that's fine, but don't blame NH for that. 
You are free to use any ORM you like (I do use EF in some projects, having choices is wonderful).
 
    Diego

Diego Mijelshon

unread,
Apr 17, 2012, 7:55:30 AM4/17/12
to nhibernate-...@googlegroups.com
Perdón, debo haber estado hablando en español por error cuando expliqué que tu código no tenía nada que ver con el ticket que mencionaste.
 
    Diego

Ramon Smits

unread,
Apr 17, 2012, 8:05:27 AM4/17/12
to nhibernate-...@googlegroups.com

John,

1. Your example does not Dispose the session when you are finished. This is something that must be done in *all* situations.
2. NHibernate more or less only works correctly when using the NHibernate transaction AFAIK. That is the pattern NHibernate is using no matter what environment.
3. The reason why you should not create a session out-side of the transaction is becuase you could trigger modifications in the first level cache which would then not be done within a transaction. Reads must also be part of the same transaction.
4. AFAIK You cannot use 2nd level cache when using distributed transactions.


I agree that it is too bad that NHibernate does not have implicit transactions when a Session is created within a TransactionScope but it isn't really that hard to put this logic especially when you consider that you should just always immediately begin an nhibernate transaction after you have created the session.

--
Ramon

Ramon Smits

unread,
Apr 17, 2012, 8:07:03 AM4/17/12
to nhibernate-...@googlegroups.com
On Tue, Apr 17, 2012 at 1:55 PM, Diego Mijelshon <di...@mijelshon.com.ar> wrote:
Perdón, debo haber estado hablando en español por error cuando expliqué que tu código no tenía nada que ver con el ticket que mencionaste.

"Sorry, I have been speaking in Spanish by mistake when I explained that your code had nothing to do with the ticket you mentioned."

??

:-)

John T

unread,
Apr 17, 2012, 9:01:15 AM4/17/12
to nhibernate-...@googlegroups.com
*round of applause for the sharp wit*

Regards,
J.

On Tuesday, April 17, 2012 12:55:30 PM UTC+1, Diego Mijelshon wrote:
Perdón, debo haber estado hablando en español por error cuando expliqué que tu código no tenía nada que ver con el ticket que mencionaste.
 
    Diego


John T

unread,
Apr 17, 2012, 9:07:28 AM4/17/12
to nhibernate-...@googlegroups.com
Thank you. This is the kind of response I hoped to get. :)

I neglected to dispose the session in my example, yes. However in the "real" code we utilise WCF for handling sessions (i.e. within the ReleaseInstance() event on the InstanceProvider, for those people who are familiar with WCF).

Point 2, 3 and 4 is what I would like to improve. I would like NHibernate to be better integrated with TransactionScope, so I don't have to create a new Session/explicitly start NH Transaction for every TransactionScope*. I want to help it get there, too. I am not just stamping my feet demanding "you" do it for me! :)

Regards,
J.

*Would I have to explicitly start a new NH Transaction for any n+1 TransactionScopes I use for the same Session?

Gerke Geurts

unread,
Apr 18, 2012, 4:13:39 AM4/18/12
to nhibernate-...@googlegroups.com
It looks like most calls of NHibernate session methods indirectly call AbstractSessionImpl.EnlistInAmbientTransactionIfNeeded which delegates to ITransactionFactory.EnlistInDistributedTransactionIfNeeded. So you may be able to achieve what you want by implementing a custom transaction factory and registering it in the NHibernate configuration.
 
Regards,
Gerke.

Roy Jacobs

unread,
Apr 18, 2012, 5:24:16 AM4/18/12
to nhibernate-...@googlegroups.com

4. AFAIK You cannot use 2nd level cache when using distributed transactions.

Really? Is there some more information about this anywhere? 

Ramon Smits

unread,
Apr 18, 2012, 5:34:50 AM4/18/12
to nhibernate-...@googlegroups.com
4. AFAIK You cannot use 2nd level cache when using distributed transactions.

Really? Is there some more information about this anywhere? 

Well, you can use 2nd level cache but it can contain dirty data. AFAIK, the 2nd level cache has a tight relation with the NHibernate transaction and when the NHibernate transaction gets committed and its distributed transaction does not then the 2nd level cache contains dirty data.

I don't have any information about this but I also cannot give information that mentions that 2nd level cache does work correctly with distributed transactions.

-- 
Ramon

Reply all
Reply to author
Forward
0 new messages