We are trying to combine objects after a StaleObjectStateException has been thrown to save a merged copy.
Here's our environmental situation:
- List item
- Multi-user system
- WPF Desktop application, SQL Server 2008 database
- NHibernate 3.1.0.4000, FluentNHibernate 1.2.0.712
- Global, long-running NHibernate sessions [for the moment. We understand session-per-presenter is the recommended pattern, but do not have time in our project schedule to convert at present.]
- Top-down saves and property navigation (that is to say we save the top-level object (herein called Parent) in our domain graph)
- .Cascade.AllDeleteOrphan() used in most cases.
- Users exclusively own some objects in the domain graph, but share ownership of the Parent.
- Navigation properties on Children objects do not exist.
- All classes have numeric ID and numeric Version fields.
Use case:
- User 1 starts application and opens Parent.
- User 2 starts application and opens Parent.
- User 2 adds a child (herein C2).
- User 2 saves Parent.
- User 1 adds a child (herein C1).
- User 1 saves Parent.
- User 1 receives a StaleObjectStateException (and rightly so)
We want to gracefully handle the exception. Because the users share ownership of the parent, User 1 should be able to save successfully, and save the Parent with both his new child, and User 2's child.
When SOSE is thrown, according to Ayende (http://msdn.microsoft.com/en-us/magazine/ee819139.aspx):
your session and its loaded entities are toast, because with NHibernate, an exception thrown from a session moves that session into an undefined state. You can no longer use that session or any loaded entities
C1 has already been assigned an ID and Version # by the now-not-useful session. (I wish it had not been.)
How do we combine the use of ISession.Merge() and ISession.Refresh() to get a newly saved Parent that has both C1 and C2 ?
We have tried a number of arcane permutations, none of which fully work. Usually, either a "row was updated or deleted by another transaction (or unsaved-value mapping was incorrect" or an actual ID collision at the ODBC level.
Our theory, at the moment:
- Reset version numbers on C1 (to prevent "unsaved-value mapping was incorrect")
- Get a new session
- newSession.Refresh(C1);
- newParent = newSession.QueryOver[...]
- newParent.Add(C1);
- newSession.SaveOrUpdate(newParent)
However, all the documentation suggests that newSession.Merge is supposed to be sufficient.
Other posts used as research:
Fluent NHibernate Newbie: Row was updated or deleted by another transaction
Is there an alternative to ISession.Merge() that doesn't throw when using optimistic locking?
StaleObjectstateException row was updated or deleted by
How I can tell NHibernate to save only changed properties
Hibernate (JPA): how to handle StaleObjectStateException when several object has been modified and commited (java, but relevant, i think)