7

In NHibernate 3.0, FlushMode.Auto does not work when running under an ambient transaction only (that is, without starting an NHibernate transaction). Should it?

using (TransactionScope scope = new TransactionScope()) 
{
    ISession session = sessionFactory.OpenSession();
    MappedEntity entity = new MappedEntity() { Name = "Entity", Value = 20 };
    session.Save(entity);

    entity.Value = 30;
    session.SaveOrUpdate(entity);

    // This returns one entity, when it should return none
    var list = session.
               CreateQuery("from MappedEntity where Value = 20").
               List<MappedEntity>();
}

(Example shamelessly stolen from this related question)

In the NHibernate source I can see that's it's checking whether there's a transaction in progress (in SessionImpl.AutoFlushIfRequired), but the relevant method ( SessionImpl.TransactionInProgress) does not consider ambient transactions - unlike its cousin ConnectionManager.IsInActiveTransaction, which does consider ambient transactions.

Community
  • 1
  • 1
Jeff Sternal
  • 47,787
  • 8
  • 93
  • 120
  • Thanx for the detailed analysis above, I added it to a ticket, the fix should be in NH 4.1.x.x. – baHI Jan 29 '16 at 20:18
  • Anyhow to the code above: sometimes you cannot avoid to save then read from the same transaction. **But in your case (in most of the cases), there's no need to do the read inside that transaction.** Another possibility is to do session.Flush() before reading. I know it's something NHibernate should do, but... – baHI Jan 29 '16 at 20:19

4 Answers4

6

Good news. Thanks to Jeff Sternal (who nicely identified the problem) I updated https://nhibernate.jira.com/browse/NH-3583 and thanks to the NH staff, there's already a fix and a pull request so in the upcoming release 4.1.x.x this ISSUE will be fixed.

baHI
  • 1,510
  • 15
  • 20
3

You should use an explicit NHibernate transaction always.

using (TransactionScope scope = new TransactionScope()) 
using (ISession session = sessionFactory.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
    //Do work here
    transaction.Commit();
    scope.Complete();
}

I see you also wrote in the NH dev list - while this can change in the future, that's how it works now.

Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • 1
    Without arguing about the merits of that guidance (which Ayende has disputed on several occasions), it doesn't answer my question, which is not about how the code currently works. It's really about ideal behavior: would auto-flushing under an ambient transaction only even be desirable? Conversely, is there any reason NHibernate (or really, any other ORM) should **not** auto-flush under an ambient transaction? – Jeff Sternal Mar 06 '11 at 02:29
  • I think in some of his posts Ayende also mentions that ambient transactions works. So if you use ambient transactions and you nest them, the above code will lead to errors, like @brad describes with oracle. Anyhow guys already fixed that error and when NH 4.1.x.x rolls out, everything will work... – baHI Jan 29 '16 at 20:11
2

The answer provided by Diego does not work in the case where you have an oracle database. (releated question). The session.BeginTransaction will fail because the connection is already part of a transaction.

Looks like we have to write some code around this problem in our application (WCF,NHibernate, Oracle), but it just feels like something that NHibernate should provide out of the box. So if anyone has a good answer, it would be really appreciated.

Community
  • 1
  • 1
3komma14
  • 168
  • 1
  • 7
0

For me, I don't know the reason behind, but by forcing session flush before session is disposed seemed to worked for me. e.g. using(session) { //do your work

session.Flush(); }

I verified that this works with my distributed transaction, since without doing this, I always get "transaction has aborted" when TransactionScope is disposed.

Even set session.FlushMode to Commit did NOT work for me.

  • Yes, with distributed transaction even autoflush on commit will fail. I think there's also some problem with threads. There's a code what inside NServicebus was used: Thread.Sleep(10) :D There' – baHI Jan 29 '16 at 20:23