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

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

Related

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

Core data : how to undo operations once managed objects are saved with context

I am trying to implement downloading of bulk data from several tables on the server.
In my case there are 16 tables. For all these tables I will be firing 10 requests to the server. This means I have done a bit of logical groupings for related tables, but it is like all tables are inter-related with each other through one or the other relationship.
I need to consider three cases while doing downloading:
Saving data to each table at local.
Managing relationships between inserted objects.
Handling situation when one of the requests fails during download, say 8th request failed.
I will be following this approach for each response:
Inserting data in managed object context.
Managing relationships by firing NSPredicate and associating the related objects.
Saving the context.
In case of a response failure, I have two options:
Next time continue from the failed response.
Revert all saved data to its previous state.
1st approach may lead to some data inconsistency, so I am going with 2nd approach.
I know that if a managed object context is not saved, we can revert the changes, but
is it possible to revert the changes, if the managed object context is
saved?
I require some useful answers from the community.
Please suggest.
Is it possible to revert the changes, if the managed object context is saved?
After saving? Maybe, but it could be tricky. If you set up a separate managed object context for your network operations, and give it an NSUndoManager, you could later on tell the undo manager to roll everything back to the previous state.
It would be simpler to just not save changes until you're finished, though. Using an undo manager doesn't really help much-- the memory needed to store up all the undo actions will at least match the memory use from keeping all of the unsaved changes around until you're finished. If you're working on a separate managed object context (whether a child context or a completely separate context), handling the error case is as simple as letting the MOC get deallocated without saving changes first.

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

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!

lazy-loading fails when session got disconnected

We use one (read-only) session which we disconnect as soon as we retrieve the data from the database. The data retrieved, often has lazy-loaded properties which are not initialized yet.
When we try to access the properties, the following exception gets thrown:
NHibernate.LazyInitializationException
Initializing[NHibernateTest.AppUser#16]-failed to lazily initialize a collection of role: NHibernateTest.AppUser.Permissions, session is disconnected
Is there a way (interceptor) to automatically detect that the application is trying to access an uninitialized property, so that the interceptor can quickly open the connection and close it after the unit of work?
Fetching everything at once would nullify the usage of laziness.
There is no efficient way to do that. The idea is that you keep the session open until your done with the session. There should be one session per unit of work. (a session is kind of unit of work actually).
Fetching everything your need in one query is more efficient than fetching everything you need in multiple queries, so I don't agree with your last statement. Lazy loading is useful for lazy programmers (like me) but is never more efficient than eager loading. Lazy loading can save you some programming time, but you still have to watch out for to many queries being executed (select N+1)

NHibernate concurrency / cache problem

I have two applications running on a machine, where NHibernate is used as an ORM. One app is managing objects (CRUD operations), while the other is processing the objects (get, process, set status and save).
First I let the processing app process an object and set the status to processed. Then I change a text property manually in the database and reset the status (to make it process it again). The manual DB edit is to simulate the managing app. Then I start to see problems:
The read object still has the old text property, event though I've changed it in the DB. I guess NHibernate caching is the problem here.
When I set the object's status to processed, it uses all properties in the where clause when updating, which means it doesn't get updated in the database. This is because it has the wrong text in a property. I would guess this also has to do with caching.
The consequence of the status not being updated is that the same object (with wrong text) is processed over and over and over...
Anyone out there who can help me with how I should set up NHibernate to make this problem disappear?
Better call refresh method on the object you want, because flush can have unwanted side-effects.