Sunday, March 27, 2011

Nhibernate: Handling an ITransaction Exception So That New Transactions Can Continue with same ISession

I have a list of 10 data objects that I want to insert/update to the database using NHibernate. If one throws an exception (say a primary key violation) I want to still insert/update the other 9. I rolled each object operation into its own atomic transaction, and roll back the transaction if there is an exception. Problem is that if a transaction does cause an exception and is rolled back, on the next transaction Nhibernate complains with the error: null id in Nexus.Data.PortfolioCorporateEntity entry (don't flush the Session after an exception occurs)

My main program is simple. It creates a session from a sessionfactory, creates the data access layer, does some work on the data objects and then tries to persist those data objects to the database.

    sessionsManager = new NHibernateSessionManager();
        session = sessionsManager.GetSession();
        DALC = new NHibernateDataProvider(session);

                            …

        foreach (var pce in pces)
        {
            try
            {
                DALC.UpdateOrAddObject<PortfolioCorporateEntity>(pce);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not add Corporate Entity ID " + pce.CorporateEntity.CorporateEntityID.ToString());
            }

        }

This is the updateOrAdd procedure in my Nhibernate Data Access Layer, called 10 times for 10 objects.

public void UpdateOrAddObject(T workObject) { using (ITransaction tx = mSession.BeginTransaction) { try { mSession.SaveOrUpdate(workObject); mSession.Flush(); tx.Commit(); } catch (Exception ex) { tx.Rollback(); throw ex; } } }

Just to make the point clear, the session is instantiated by the calling program and passed to the Data Access Layer object, constructor of which is below.

public NHibernateDataProvider(ISession session) { mSession = session; }

This works fine except after the exception, it says don’t flush the session after exception. I’m not sure why – transaction was rolled back nicely and the database should be ready to accept another transaction no? What am I doing wrong?

From stackoverflow
  • It's not possible to re-use an NHibernate session after an exception is thrown. Quoting the documentation:

    If the ISession throws an exception you should immediately rollback the
    transaction, call ISession.Close() and discard the ISession instance.
    Certain methods of ISession will not leave the session in a consistent state.
    

    So the answer is that you can't do what you're trying to do. You need to create a new session and re-try the updates there.

  • Hey Sean,

    Thanks for the response. Just wanted to make sure it's done right. What you're saying is that my error handling should be simply changed to:

            foreach (var pce in pces)
            {
                try
                {
                    DALC.UpdateOrAddObject<PortfolioCorporateEntity>(pce);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not add Corporate Entity ID " + pce.CorporateEntity.CorporateEntityID.ToString());
    
                    session.Close();
                    session = sessionsManager.GetSession();
                    DALC.Session = session;
    
                }
            }
    

    Looks like this works just fine. Thanks.

    Sean Carpenter : From what you posted, that looks like it will work. It really depends on the implementation of your DALC class.
  • I clear the session and it continues normally

    
    ISession session = NHibernateHelper.Session;
    using (ITransaction transaction = session.BeginTransaction())
    {
        try
        {
            session.Update(user, user.UserID);
            transaction.Commit();
        }
        catch (Exception ex)
        {
            transaction.Rollback();
            session.Clear();
            throw new DALException("Cannot update user", ex);
        }
    }
    
    

0 comments:

Post a Comment