'possible nonthreadsafe access to session' after deletion rollback

785 views
Skip to first unread message

fancyplants

unread,
Oct 7, 2009, 10:04:48 AM10/7/09
to nhusers
Hi all,

I am getting a persistent error with a quite complex app I am
writing. NHibernate is very powerful but I dont think I'm using it
properly.

Because the system is so complex I'll try to be brief.

I am using NHibernate and VB.NET in a thick-client app.

I have entities called Venue and VenueRoom. A Venue has many rooms,
along with some other entities with similar relationships.

I am using NHibernate in what I can only describe as 'session by
screen' - ie when the user requests a screen (windows form/dialog) in
the app, it gets its own session that is discarded when the screen
closes. (is this a really bad thing?)

The parent screen calls the venue screen (showing a single venue + its
rooms). The user can add/edit/delete rooms, but if they then cancel
the screen, the changes are ignored. I do this by beginning a
transaction before opening the screen, and then either committing or
rolling it back depending on whether the user pressed Save or Cancel.

Right. The problem:

If I create/edit/delete rooms and press Save, everything is fine.

But, if I:
1. Delete an existing room
2. Press Cancel
3. Display the screen a second time with the same venue (the deleted
room is back as expected)
4. Press Save (with no editing)

the 'possible nonthreadsafe access to session' error is thrown.

My xml: (in clsVenue.hbm.xml)

<set name="Rooms" cascade="all" inverse="false">
<key column="VenueID" not-null="true"/>
<one-to-many
class="Bookings_System.clsVenueRoom,Bookings_System"/>
</set>

Code in the screen (when the screen is shown)

Dim l_venue As clsVenue =
VenueManager.getInstance.getVenue(l_venueID, getSession())

Dim l_trans = getSession().beginTransaction()

lazyCreateVenueDialog()
m_venueDialog.Mode() = MODE_EDIT
m_venueDialog.setData(l_venue)
m_venueDialog.ShowDialog(Me)
If (m_venueDialog.userConfirmed()) Then
Try
' All sub objects of venues will have been
added/deleted etc
' just need to get the venue itself and fill
it with
' the edited data
l_venue = m_venueDialog.getData()
m_venueDialog.fillData(l_venue)

VenueManager.getInstance().updateVenue
(l_venue, getSession())

l_trans.commit() <---- Exception at step 4

' Update the list and the data behind it
refreshData()
Catch ex As Exception
l_trans.rollback()
' also revert the object back to its original
state
getSession().Evict(l_venue)

MsgBox("Cannot Edit Venue:" + ex.Message())
End Try
Else
If l_trans IsNot Nothing Then
l_trans.rollback()
' also revert the object back to its original
state
getSession().Evict(l_venue)
End If
End If

The Stack Trace:

at NHibernate.Action.EntityDeleteAction.Execute() in c:\CSharp
\NH2.1.x\nhibernate\src\NHibernate\Action\EntityDeleteAction.cs:line
81
at NHibernate.Engine.ActionQueue.Execute(IExecutable executable) in
c:\CSharp\NH2.1.x\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line
130
at NHibernate.Engine.ActionQueue.ExecuteActions(IList list) in c:
\CSharp\NH2.1.x\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line
113
at NHibernate.Engine.ActionQueue.ExecuteActions() in c:\CSharp
\NH2.1.x\nhibernate\src\NHibernate\Engine\ActionQueue.cs:line 151
at
NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions
(IEventSource session) in c:\CSharp\NH2.1.x\nhibernate\src\NHibernate
\Event\Default\AbstractFlushingEventListener.cs:line 241
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush
(FlushEvent event) in c:\CSharp\NH2.1.x\nhibernate\src\NHibernate\Event
\Default\DefaultFlushEventListener.cs:line 19
at NHibernate.Impl.SessionImpl.Flush() in c:\CSharp\NH2.1.x
\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 1477
at NHibernate.Transaction.AdoTransaction.Commit() in c:\CSharp
\NH2.1.x\nhibernate\src\NHibernate\Transaction\AdoTransaction.cs:line
187
at Bookings_System.DB.DataAccessSession.confirmTransaction
(ISessionTransaction& p_trans) in C:\Documents and Settings\david.smith
\My Documents\Visual Studio 2008\Projects\Bookings_System_svn\Bookings
System\Bookings System\Classes\DB\DataAccessSession.vb:line 56
at Bookings_System.Forms.frmVenueList.btnEdit_Click(Object sender,
EventArgs e) in C:\Documents and Settings\david.smith\My Documents
\Visual Studio 2008\Projects\Bookings_System_svn\Bookings System
\Bookings System\Forms\frmVenuesList.vb:line 396

Can anyone tell me why this is failing? I originally figured it was
something to do with it being on the event dispatch thread, but then
none of it should work.

Any suggestions appreciated (including a big list of the ways I'm
using hibernate wrong :) ).

Regards,
FP

Jason Meckley

unread,
Oct 7, 2009, 1:20:42 PM10/7/09
to nhusers
session cannot cross threads. just like UI updates cannot cross
threads. You may be passing the session object, or code that uses the
session object across threads. instead of associating a session to a
screen looking into the Session Per Business Context. I believe this
is part of NHAddIns. I have used NH with asp.net and messaging
frameworks. here is my approach to session managment

asp.net = session per view. open on begin request, dispose on end
request
messaging = open session when message arrives, dispose on message
processing complete. with the messaging framework I use this closely
aligns to a session per thread.

NH has the concept of CurrentSessionContext. there are a number of
implementations built into NH Web, ThreadStatic, MappedThreadStatic
and a few others. Web naturally works with asp.net. for my messaging
needs I use either ThreadStatic or Mapped depending on the number of
SessionFactories in my application. by utilizing these contexts my
session management looks like this:

ThreadStaticContext.Bind(factory.OpenSession());

var session = factory.GetCurrentContext();

var session = ThreadStaticContext.Unbind(factory);
if(session == null) return;
session.Dispose();

where ThreadStaticContext.Bind and ThreadStaticContext.Unbind happen
in a context defining object (HttpModule, MessageModule, etc) and
factory.GetCurrentContext() can be called from any of the objects
referenced within the context defining object.

Fabio Maulo

unread,
Oct 7, 2009, 1:41:14 PM10/7/09
to nhu...@googlegroups.com
 getSession().Evict(l_venue)
 MsgBox("Cannot Edit Venue:" + ex.Message())
brrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr!!!

2009/10/7 Jason Meckley <jasonm...@gmail.com>



--
Fabio Maulo

David Smith

unread,
Oct 8, 2009, 4:23:52 AM10/8/09
to nhu...@googlegroups.com
Hi Fabio,

Sorry, I'm having trouble working out what you mean by this.  I am evicting the venue at this point because the edits made to it should be discarded and the original data loaded from the database.

Please tell me why that is wrong, rather than just making a buzzing sound.

FP

2009/10/7 Fabio Maulo <fabio...@gmail.com>

fancyplants

unread,
Oct 8, 2009, 4:52:47 AM10/8/09
to nhusers
Hi Jason,

Thanks for looking at this and providing suggestions. I took the
session per screen idea because I was trying to get around the lazy
loading problems, but I guess it was pretty heavy handed.

However I can't understand why multiple thread access would cause this
problem. With the current code, I can create a venue, add venue
rooms, delete rooms, and everything else with no problems. Surely if
threading was the root cause of this, none of it would work.

1. Delete an existing room
2. Press Cancel
3. Display the screen a second time with the same venue (the deleted
room is back as expected)
4. Press Save (with no editing)

The only time the problem arises is when I do a room delete, then
cancel, then save again. If I replace step 1 with either editing or
adding a room, it still works fine.

The stack trace EntityDeleteAction.execute line gives me suspicions.
If I have evicted the old venue object at step 2, then the session
cache will have that old venue information discarded, and rolling back
will have cancelled the delete, so why is NHibernate trying to delete
something?

I will look into modifying my code so that the session is created only
for obtaining the data, at which point it is closed. However, if I am
using a transaction like above, surely I need to have the associated
session open during that transaction lifetime (ie when the user has
added/edited/deleted rooms in the child venue screen). Can you share
transactions between sessions?

Regards,
FP

Jason Meckley

unread,
Oct 8, 2009, 6:22:00 AM10/8/09
to nhusers
I believe you can use System.Transactions.SessionScope across threads.
(I think). but NH Transactions, no as they are directly related to the
session. I find it's a good idea to map my domain objects to view
model objects. and return the view model objects. this keep NH
(including NH proxied entities ) away from the GUI. This makes it
easier to move to multi-threading as well.
Reply all
Reply to author
Forward
0 new messages