NHibernate: is it valid to call Session.Flush() before committing a transaction?

Notoriousxl picture Notoriousxl · Mar 28, 2011 · Viewed 10.5k times · Source

I need to execute two operations inside a NHibernate's unit of work: a transactional one (an entity saving) and a not-transactional one.

Since the not-transactional operation cannot be rollbacked, if I save the entity before executing the not-transactional operation (and finally committing the transaction) I still get a transactional behaviour:

  • The operations will be committed only if the two sub-operations execute successfully;
  • If the entity save fails, the not-transactional operation won't be executed;
  • If the not-transactional fails, the entity save will be rollbacked.

The problem: with a code like the following, NHibernate won't execute the actual sql insert until the call to transaction.Commit() (which calls session.Flush() internally):

        using (var transaction = session.BeginTransaction())
        {
            session.Save(entity);
            NotTransactionalOperationBasedOn(entity);

            transaction.Commit(); // the actual sql insert will be executed here
        }

With a code like this, if the Sql Insert fails it's too late: NotTransactionalOperation has been executed. To execute the actual SQL insert before executing NotTransactionalOperation I have to explicity call session.Flush() right before the session.Save():

        using (var transaction = session.BeginTransaction())
        {
            session.Save(entity);
            session.Flush(); // the actual sql insert will be executed here

            NotTransactionalOperationBasedOn(entity);

            transaction.Commit(); 
        }

The code works, but... is it considered a best practice to call session.Flush() before committing the transaction? If not, are there better ways to achieve the same result?

Answer

Frederik Gheysels picture Frederik Gheysels · Mar 28, 2011

Flushing means that NHibernate will make sure that all changes are persisted to the DB. That is, it will make sure that all necessary SQL statements are executed. When the transaction fails, and is thus rollbacked, all those changes will be reverted. So, I see no problem in doing that (you must keep in mind however, that your entity might not be in a valid state, since the transaction has failed).

But ... I don't see the problem actually: When the non-transactional procedure fails, what's the problem ? Since the procedure is non-transactional, I guess it has no influence on the information that is held by the database ? Or, what does this code do ?

If it is non-transactional, why can't you do it outside your unit-of-work ?

When the save fails, I guess you'll make sure that not only the transaction is rolled-back, but the exception will be thrown upon the stack as well, until it encounters an error-handler, which probably means that your non-transactional code will not be executed as well:

using( var transaction = session.BeginTransaction() ) 
{
      repository.Save (entity); 

      transaction.Commit();   // error, exception is thrown.
}

NonTransactionalCode (entity); // this line will not be executed, since the exception will be thrown up the stack until a suitable catch block is encountered.