tags:

views:

917

answers:

3

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?

+3  A: 

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.

Sean Carpenter
A: 

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.

From what you posted, that looks like it will work. It really depends on the implementation of your DALC class.
Sean Carpenter
A: 

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);
    }
}

sh_kamalh