开发者

Flush NHibernate whilst still allowing transaction rollback

I am trying to use NHibernate with legacy entities that are not map开发者_如何转开发ped with NHibernate. On occasion this means that I need to manually flush NHibernate data to the database so that I don't receive foreign key exceptions when I try to connect the legacy entities with NHibernate-mapped entities.

A problem occurs when this takes place within a transaction that then needs to be rolled back. The data flushed from NHibernate does not rollback.

Is there anything I can do about this?

UPDATE

Still curious how to do this - I don't believe either of the answers given address the issue. I need to call Flush(). The question is, how do I rollback data that has been flushed?


check this: Force query execution without flush/commit

I seemed to have the same problem, i would flush and then i would rollback but some data would remain persisted in the database. However, there were some parts in my code that would call a commit, which cannot be rolled back. Consider the accepted answers' code snippet as the proper usage for transactions, flushes, rollbacks and commits and take into consideration that this pattern can be extended...

in a single unit of work (ie we consider a Request in a web application as a single unit of work and everything that happens in that request exists in a single transaction which onEndRequest is committed):

  1. you call _sessionFactory.OpenSession(), _session.BeginTransaction(), _session.CommitTransaction() and _session.CloseSession() only once.

  2. you can call _session.Flush() and _session.RollBackTransaction() as many times as you want, but Flush() is automatically called on Commit automatically. You may want to call a Flush when you need to make a query and ensure that the data fetched will not be stale.

  3. Note that once a commit transaction is committed, all operations afterwards do not happen on that transaction. Instead NHibernate will create the necessary transaction under the hood (http://www.nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions) in which point you already have problems tracking consistency and possibly logical integrity

  4. If you really must call commit in the middle of your unit of work it is strongly advised to create a new transaction at that point so you can manage it explicitly

  5. What's even better is to try out Nested Transactions will allegedly allow partial commits; you can rollback the "root" transaction and all changes will be reverted. I haven't really tested this feature of .NET and SQL Server, although the nested transaction in the database itself leaves a lot to be desired and i don't know how exactly ADO.NET instruments this feature.

points 1 to 4 have been tested with all versions of NHibernate starting from 1.2.


For the sake of formatting I allow myself to update tolism7's answer here.

  1. use using and forget about transaction.Dispose() - the transaction will automatically be Dispose'd of at the end of the using block.
  2. throw - don't throw ex because it means throwing away your stacktrace (see this post where it states "When the .NET Framework executes this statement: throw ex; it throws away all the stack information above the current function.")

.

public void CommitChanges()
{
    using (var transaction = Session.BeginTransaction())  // <-- open scope
        try
        {
            // do something
            transaction.Commit();
        }
        catch (HibernateException)
        {
            transaction.Rollback();
            _session.Close();
            _session.Dispose();

            throw;  // <-- this way the stacktrace stays intact!
        }
}

A VB.NET version of this piece of code can be found here.


When using transactions with NHibernate try to avoid using the Session.Flush() and instead use the transaction.Commit() which it calls the session.flush() internally.

If during the Commit() an error occurs and the transaction needs to be rolled back this can be addressed like this.

public static void CommitChanges()
{
    ITransaction transaction = Session.BeginTransaction();

    try
    {
        transaction.Commit();
    }
    catch (HibernateException ex)
    {
        transaction.Rollback();
        //close and dispose session here
        throw ex;
    }
    finally
    {
        transaction.Dispose();
    }
}

Now, if a manual call to flush() or a call to commit() goes through successfully there isn't a way to roll back the transaction using NHibernate mechanisms. Especially when calling the transaction.Commit() command the AdoTransaction created by NHibernate is then disposed right after the Commit() finishes so you cannot access it in order to roll back.

The code sample above allows you to catch errors that happen during commit and then roll back the transaction that has started already.

Now instead of calling the transaction.Commit() in the sample above you call the session.Flush() in my tests no data are saved in the Database as the transaction is never commited.

I have no idea how your code looks like but if you are calling in a pattern, as the above the code sample shows, the transaction.commit() instead of the Session.Flush() it should give you a way to achieve what you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜