Why is NHibernate saving objects when I never execute session.Save? - nhibernate

I'm using NHibernate with Fluent NHibernate.
I have code where I start a transaction, then I enter a loop which creates several objects. For each object I check certain conditions. If these conditions are met, then I execute a session.SaveOrUpdate() on the object. At the end of the loop, I issue a commit transaction.
I have a breakpoint set on the session.SaveOrUpdate command, proving that it is never reached (because the conditions have not been met by any of the objects in the loop). Nevertheless, when the transaction is committed, the objects are saved!
I am using an AuditInterceptor and have set a breakpoint in the OnSave method. It is being called, but the stack trace only traces back to the statement that commits the transaction.
There are no objects of any kind that have had SaveOrUpdate executed on them at this point, so cascading doesn't explain it.
Why is NHibernate saving these objects?

From NHibernate ISession.Update thread:
It's the normal and default behavior:
Hibernate maintains a cache of Objects
that have been inserted, updated or
deleted. It also maintains a cache of
Objects that have been queried from
the database. These Objects are
referred to as persistent Objects as
long as the EntityManager that was
used to fetch them is still active.
What this means is that any changes to
these Objects within the bounds of a
transaction are automatically
persisted when the transaction is
committed. These updates are implicit
within the boundary of the transaction
and you don’t have to explicitly call
any method to persist the values.
From Hibernate Pitfalls part 2:
Q) Do I still have to do Save and
Update inside transactions?
Save() is only needed for objects that
are not persistent (such as new
objects). You can use Update to bring
an object that has been evicted back
into a session.
From NHibernate's automatic (dirty checking) update behaviour:
I've just discovered that if I get an
object from an NHibernate session and
change a property on object,
NHibernate will automatically update
the object on commit without me
calling Session.Update(myObj)!
Answer: You can set Session.FlushMode to
FlushMode.Never. This will make your
operations explicit ie: on tx.Commit() or session.Flush().
Of course this will still update the
database upon commit/flush. If you do
not want this behavior, then call
session.Evict(yourObj) and it will
then become transient and NHibernate
will not issue any db commands for it.

It's to do with the sessions flush mode being FlushMode.Commit (default). When the transaction is committed any changes made to objects within the session are saved and the changes persisted.
There's a FlushMode property on the session that you can set. If you want a readonly transaction specify FlushMode.Manual.
Hope this helps!

Related

nhibernate RollbackTransaction and why to dispose of session after rollback?

I am working on a system using nhibernate, and I see a lot of the following two lines in exception catch blocks:
session.Flush();
session.RollbackTransaction();
I am very confused by this logic, and it looks like unnecessary work to flush changes, then use transaction rollback practices.
I wanted to setup an argument for removing these flush calls and relying on just the RollbackTransaction method, but I came across this question. Next I read more into the documentation linked, and read the following nugget of information:
If you rollback the transaction you should immediately close and discard the current session to ensure that NHibernate's internal state is consistent.
What does this mean? we currently pair our Session life time with our web request's begin and end operations, so I am worried that the reason we are calling flush THEN rollback is to keep the session in a valid state.
Any ideas?
NHibernate does object tracking via the session, and all the changes you do the entities are stored there, when you do the flush those changes are written to the db. If you get an exception while doing so the session state is not consistent with the database state, so if you do a rollback at this stage it will rollback db transaction but session values will not be rolled back.
As per design once that happens the session should not be used further in reliable manner (even Session.Clear() will not help)
If you use the session per request and if you get an error the best approach is to display an error to the user and ask to retry the operation. Other option is to create a brand new session and use it for data fetching purposes to display errors.
This Flush before Rollback is very likely a trick for working around a bug of the application caused by re-using the session after the rollback.
As you have found by yourself, the session must not be used after a rollback. The application is doing that mistake as per your comment.
Without the Flush before the Rollback, the session still consider the changes as pending, and will commit them at next Flush, defeating the Rollback purpose. Doing that Flush before the rollback causes pending changes to be flushed, then rollback-ed, helping avoiding the session to flush them later.
But the session is still not in a consistent state, so by continuing to use it, the application stays at risk. The session cache still holds the changes that were attempted then rollback-ed. The session does just no more consider them as pending changes awaiting a flush. If the application later usages of the session access those entities, their state will still be the modified one from the rollback-ed transaction, although they will not be considered dirty.

Can a NHibernate transaction be continued after an exception?

I am using NHibernate to save objects that require that one of the properties on these objects must be unique. The design of the system is such that it is possible that an attempt may be made occasionally to save the same object twice. This of course causes a violation of the uniqueness constraint and NHibernate throws an exception. The exception happens at the time I am attempting to save the object, rather than at Transaction.Commit() time. When this happens I want to simply catch the exception, discard the object and continue on saving other similar objects. However, I have not found a way to allow this to happen. Once that exception has happened I cannot carry on to save other objects and commit the transaction.
The only work-around I have found for this is to always check if the object exists first by running a query on the unique property. That works, but it seems unnecessarily expensive. I would like to avoid that extra hit to the db. Is there a way to do this?
Thanks
The issue, you've described, must be solved on a higher level then NHibernate session. Take a look at 9.8. Exception handling, extract:
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, what I would suggest, wrap the call to your Data layer (DL) with some validation. Place the if/try logic outside of the Session.
Because even in case, that we are using versioning (see 5.1.7. version) (a very powerful way how to survive concurrency) ... we are provided with StaleExceptions and have to solve them outside of the DL

Why evicting objects from Session does not commit changes to database?

I am using NHibernate in my application and have the following situation. (Note that the situation here is much more simplified for the sake of understanding)
An object is queried from the database. It is edited and updated using Session.Update(). The object is then Evicted from the Session (using Session.Evict(obj)) before the transaction is committed. Expected result is that the changes are persisted to the database.
This used to work fine when I had my Id column as a NHibernate identity column.
Recently, we changed the Id column to be non-identity. As a result, the above scenario does not persist to the database unless I explicitly call Session.Flush() before I Evict.
Can someone point/explain to me the reason for this behavior?
This link, NHibernate Session.Flush & Evict vs Clear, mentions something about Evict and the identity column, which to me is not very clear.
Your workflow is not correct.
First, when you retrieve an object from the database, session.Update(entity) does not do anything. Changes will happen automatically on Flush/Commit
Next, Evict removes all knowledge the session has of the object, therefore it will not persist any changes applied to it. You should almost never use this method under normal conditions, which makes me think you are not handling the session correctly.
Third, the fact that using identity causes inserts to happen immediately on Save is a limitation, not a feature.
The correct workflow is:
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var entity = session.Get<EntityType>(id);
entity.SomeProperty = newValue;
transaction.Commit();
}
The exact structure (using statements, etc) can change in a desktop application, but the basic idea is the same.
Identity forces NHibernate to immediatly save the entity to the database on session.Save() non identity allows it to batch the inserts to send them as a whole. Evict will remove all information of the object from the session. So while with identity it forgets about the entity it is already in the database even if the session doesnt know.
to remedy that you can
set Flushmode.Auto (forces immediate flushing)
call session.Flush() before Evict
Evict after the transaction completed
which is the best option depends on the context

NHibernate, ActiveRecord, Transaction database locks and when Commits are flushed

This is a common question, but the explanations found so far and observed behaviour are some way apart.
We have want the following nHibernate strategy in our MVC website:
A SessionScope for the request (to track changes)
An ActiveRecord.TransactonScope to wrap our inserts only (to enable rollback/commit of batch)
Selects to be outside a Transaction (to reduce extent of locks)
Delayed Flush of inserts (so that our insert/updates occur as a UoW at end of session)
Now currently we:
Don't get the implied transaction from the SessionScope (with FlushAction Auto or Never)
If we use ActiveRecord.TransactionScope there is no delayed flush and any contained selects are also caught up in a long-running transaction.
I'm wondering if it's because we have an old version of nHibernate (it was from trunk very near 2.0).
We just can't get the expected nHibernate behaviour, and performance sucks (using NHProf and SqlProfiler to monitor db locks).
Here's what we have tried since:
Written our own TransactionScope (inherits from ITransactionScope) that:
Opens a ActiveRecord.TransactionScope on the Commit, not in the ctor (delays transaction until needed)
Opens a 'SessionScope' in the ctor if none are available (as a guard)
Converted our ids to Guid from identity
This stopped the auto flush of insert/update outside of the Transaction (!)
Now we have the following application behaviour:
Request from MVC
SELECTs needed by services are fired, all outside a transaction
Repository.Add calls do not hit the db until scope.Commit is called in our Controllers
All INSERTs / UPDATEs occur wrapped inside a transaction as an atomic unit, with no SELECTs contained.
... But for some reason nHProf now != sqlProfiler (selects seems to happen in the db before nHProf reports it).
NOTE
Before I get flamed I realise the issues here, and know that the SELECTs aren't in the Transaction. That's the design. Some of our operations will contain the SELECTs (we now have a couple of our own TransactionScope implementations) in serialised transactions. The vast majority of our code does not need up-to-the-minute live data, and we have serialised workloads with individual operators.
ALSO
If anyone knows how to get an identity column (non-PK) refreshed post-insert without a need to manually refresh the entity, and in particular by using ActiveRecord markup (I think it's possible in nHibernate mapping files using a 'generated' attribute) please let me know!!

NHibernate NonUniqueObjectException when reattaching objects to the session (with Lock)

Basic order of execution:
A collection of PersistentObjects is queried then cached separately from the session.
The collection is passed to a module that needs to reattach them to the session in order to lazily load some of the properties (using session.Lock(obj, LockMode.None)).
After the module has completed processing, another module attempts to SaveOrUpdate a UserSetting object with some usage statistics for the user who initialized the action.
On session.Flush() NHibernate throws a NonUniqueObjectException.
I've found that one way of working around this issue is to get new copies of the objects with:
obj = session.Get(obj.GetType(), (obj as PersistentObject).Id);
instead of reattaching with session.Lock. However, this is non-optimal as some of the record sets are potentially quite large, and re-getting each object individually could become a performance drag.
The object which is non-unique is a referenced object that exists only on the PersistentObject class, and not the UserSetting class. So I cannot understand why a flush would cause this exception.
I've tried evicting the cached objects after the module is done with them, but this does not help.
Does anyone know of a better way to attach objects to the session that could avoid this problem?
Can you use a fresh session (or transaction) for processing each item and for updating the UserSetting? This would probably prevent the NonUniqueException.
Cheers,
-Maarten