Unit testing and NHibernate.AssertionFailure "don't flush the Session after an exception occurs"

1,314 views
Skip to first unread message

MAMMON

unread,
Aug 15, 2008, 1:57:45 PM8/15/08
to nhusers
I have a large ordering system I've been developing for my employer
for the last year. Our entire data access layer (DAL) is based on
NHibernate.

I'm not a pro when it comes to the internal mechanics of NHibernate,
but I'm not a complete beginner either.

I recently set up unit tests for each entity class in the DAL, to test
CRUD operations. There is an NUnit [TestFixture] class for each of my
entity classes, and they each have 4 tests: Create(), Read(),
Update(), and Delete().

When an error occurs, all of my subsequent tests fail. I get the
error message:

NHibernate.AssertionFailure: null id in
CBMapping.MOS6.Data.Entities.MapStyle entry (don't flush the Session
after an exception occurs) --- (Stack trace is at end of message)

I'm really not concerned with the original error that occurred in one
of my unit tests. What concerns me is that if one of my tests fail,
all of my subsequent tests will fail with that error.

At the beginning of each test, I call session.BeginTransaction(), and
at the end of each test, I check to see if there is an open
transaction with this method:

public bool HasOpenTransaction()
{
ISession session = CurrentSession;
ITransaction tran = session.Transaction;
bool hasTran = tran != null && !tran.WasCommitted && !
tran.WasRolledBack && tran.IsActive;
return hasTran;
}

If there IS an open Transaction, I call:
session.Transaction.Rollback();

And finally, I call session.Close();

I'm not sure what's causing my ISession to Flush() after an error
occurs, but I'd really like to get my unit tests to be "independent",
so that if one fails, it doesn't cause the rest to fail. Any help is
much appreciated!

Here is the stack trace:
at
NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object
obj, IEntityPersister persister, Object id, EntityMode entityMode) in
c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs: line 112
at
NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object
entity, EntityEntry entry, EntityMode entityMode, Boolean
mightBeDirty, ISessionImplementor session) in c:\CSharp
\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs: line 81
at
NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent
event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\DefaultFlushEntityEventListener.cs: line 36
at
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent
event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\AbstractFlushingEventListener.cs: line 163
at
NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent
event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\AbstractFlushingEventListener.cs: line 61
at
NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent
event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
\DefaultFlushEventListener.cs: line 18
at NHibernate.Impl.SessionImpl.Flush() in c:\CSharp\NH2.0.0\nhibernate
\src\NHibernate\Impl\SessionImpl.cs: line 1186
at NHibernate.Transaction.AdoTransaction.Commit() in c:\CSharp
\NH2.0.0\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs: line
177
at CBMapping.Data.NHibernate.NHibernateDAL.CommitTransaction(Boolean
closeSession) in NHibernateDAL.cs: line 296
at CBMapping.Data.NHibernate.NHibernateDAL.CommitTransaction() in
NHibernateDAL.cs: line 319
at
CBMapping.MOS6.Data.UnitTests.CrudTests.OfficeGroup_CrudTest.Create()
in OfficeGroup_CrudTest.generated.cs: line 65

Ayende Rahien

unread,
Aug 15, 2008, 2:18:31 PM8/15/08
to nhu...@googlegroups.com
Create a new session, don't keep the same session around.

Will Shaver

unread,
Aug 15, 2008, 2:21:36 PM8/15/08
to nhu...@googlegroups.com
Can we see a copy of a test? Most likely you're doing something like this:
 
Test()
{
beginTranscaction()
//dostuff
HasOpenTransaction()
}
 
the problem is that if your dostuff ends up throwing an exception you'll never get to rour HasOpenTransaction. You should either do it in a using(){} statement or close your transaction in the [TearDown] method.

sbohlen

unread,
Aug 15, 2008, 2:33:56 PM8/15/08
to nhusers
My understanding of what happens when an execption is thrown from
within the ISession instance is the "the session is left in an
inconsistent and undefined state" per the docs which I have aways
taken to mean that you basically shouldn't call
session.<anything>(...) on it after an exception is thrown.

Throw away (destroy) the session object from within which the
exception was thrown and use SessionFactory to open another new
session (return a new ISession instance entirely) to do anything
else. Closing the session instance and trying to start again with
more operations from within that same original session instance isn't
allowed since by definition the 'state of the session is not
consistent'. No amount of .Close(...) or anything else will return
the session instance to a known-good state and this is why all other
tests after your initial exception will always fail in this context.
> > NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(Flus­hEntityEvent
> > event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
> > \DefaultFlushEntityEventListener.cs: line 36
> > at
>
> > NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushE­vent
> > event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
> > \AbstractFlushingEventListener.cs: line 163
> > at
>
> > NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExe­cutions(FlushEvent
> > event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
> > \AbstractFlushingEventListener.cs: line 61
> > at
> > NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent
> > event) in c:\CSharp\NH2.0.0\nhibernate\src\NHibernate\Event\Default
> > \DefaultFlushEventListener.cs: line 18
> > at NHibernate.Impl.SessionImpl.Flush() in c:\CSharp\NH2.0.0\nhibernate
> > \src\NHibernate\Impl\SessionImpl.cs: line 1186
> > at NHibernate.Transaction.AdoTransaction.Commit() in c:\CSharp
> > \NH2.0.0\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs: line
> > 177
> > at CBMapping.Data.NHibernate.NHibernateDAL.CommitTransaction(Boolean
> > closeSession) in NHibernateDAL.cs: line 296
> > at CBMapping.Data.NHibernate.NHibernateDAL.CommitTransaction() in
> > NHibernateDAL.cs: line 319
> > at
> > CBMapping.MOS6.Data.UnitTests.CrudTests.OfficeGroup_CrudTest.Create()
> > in OfficeGroup_CrudTest.generated.cs: line 65- Hide quoted text -
>
> - Show quoted text -

Will Shaver

unread,
Aug 15, 2008, 2:34:38 PM8/15/08
to nhu...@googlegroups.com
Please reply to the group so others can search our discussions later. :)
 
You can do multiple transactions in a single session. I generally choose to do my unit of work as a single session and transaction together, but that is partially an artifact of doing mvc style web work.

On Fri, Aug 15, 2008 at 11:30 AM, MAMMON <die...@gmail.com> wrote:
I will try that right now.  Also, what is the basic "Unit of Work" in
NHibernate?  Is it the Session or the Transaction?

MAMMON

unread,
Aug 15, 2008, 2:44:12 PM8/15/08
to nhusers
Calling session.Close() in the [TearDown] method work beautifully. I
had a [TearDown] method already, and I was doing this:

session.SessionFactory.Close();

However, when you read the comments for ISessionFactory.Close(),
you'll read:
Destroy this SessionFactory and release all resources connection
pools, etc). It is the responsibility of the application to ensure
that there are no open Sessions before calling close().

So I guess that wasn't closing my session after all. Killing the
whole session factory might have been overkill anyway. Just closing
the session did the trick.


Thanks guys! Quick responses!


On Aug 15, 11:21 am, "Will Shaver" <will.sha...@gmail.com> wrote:

MAMMON

unread,
Aug 15, 2008, 2:34:07 PM8/15/08
to nhusers
I will try that right now.

Also, what is the basic "Unit of Work" in NHibernate? Is it the
Transaction or the Session?

(Sorry about emailing this to you directly before -- hit "Reply to
author" instead of "Reply")

On Aug 15, 11:21 am, "Will Shaver" <will.sha...@gmail.com> wrote:

MAMMON

unread,
Aug 15, 2008, 3:16:40 PM8/15/08
to nhusers
Excellent, thank you. Closing the session in the [TearDown] method
worked just fine. I was originally closing the SessionFactory in the
[TearDown] method, but according to the documentation, it is the
responsibility of the program to make sure there are no open Sessions
before closing the session factory.

Sorry if there are multiple replies, something awry was going on
trying to reply earlier (also, sorry about the personal email reply
Will).
Reply all
Reply to author
Forward
0 new messages