NHibernate FlushMode - Why Can't NH Evict Everything Not Saved? - nhibernate

I've spent some time searching around how to configure NHibernate's FlushMode so it could only save objects that I've explicity called Save/Update/Delete, but I've figured out that I can't do that. Instead of it, I have to evict every object that I've modified (even without calling Save/Update/Delete) as I'm using NHibernate transaction management.
I understand perfectly why NHibernate have to Flush some objects before some Find operations, but I'm not worried about stale data. I see that, maybe, in some situation, flush everything that was modified and not explicity saved can be usefull, but it's not my case.
I simply want that, after commiting my session, NHibernate inserts/updates/deletes everything that I have explicitly demanded it to, and evict everything else. My question is: is this behavior just a question of "nobody stopped to implement this yet" or are there another points that would fail if this kind of behavior existed?
Thank you in advance.
Filipe

Nhibernate doesn't think that way. The distinction is between transient and persistent objects, and persistent objects are synchronized with the database when the session is flushed (an possibly at other times). Objects that are retrieved using NH are persistent and will be saved when the session is flushed without calling Save (or SaveOrUpdate) because they are already persistent. There are several options for controlling the FlushMode but nothing that would make it work the way you desire.
A potential workaround might be to retrieve objects using an IStatelessSession and handle operations through a separate ISession.
What problem are you trying to solve?

You are basically asking: "why doesn't my hammer work more like a screwdriver?"
The whole idea of the Session (among other things) is to allow automatic dirty tracking, so you don't need to worry about what was changed; only additions and non-cascaded deletions are manual.
As Jamie mentioned you can use IStateLessSession instead of ISession. It does not track anything automatically and it does not support lazy loading. You have to tell it explicitly what to insert, update and delete. It's more used for read-only and batch contexts, but it's probably what you're looking for,

Related

Have NHibernate ignore not set properties?

I have a huge object, it has a lot of lazy loadable properties.
I want to enable a quick edit of a very small subset of its property.
How can I, when I just have a few values, tell NHibernate: don't touch anything else?
Because now, when I save, everything not set gets lost.
Have you tried dynamic-update option on your class mapping?
<class name="SomeEntity" dynamic-update="true">
But check if the flush does not cause the unloaded lazy properties to get loaded first, just in case.
In your question, you state you lose other properties. I have never witnessed such a behavior. Are you attaching (using ISession.Update or ISession.Merge) a detached entity in your current code?
What I am suggesting will not work in such a case. It should instead work with an entity loaded from the current ISession, touched on some properties then saved to db only using ISession.Flush (or preferably, ITransaction.Commit, since it is not a good practice to work without transactions).

Forbid object changes outside of a transaction

we recently stumbled over a behaviour of NHibernate that gave us a real headache ;).
Lets look at the following example:
declare x
open transaction
create and persist object x
commit transaction
change object x
open other transaction
do nothing
close transaction
It hit us as a surprise, that the changes made to 'x' outside of the second transaction where commited to the database (although this behaviour makes also sense to me, after i thought about it). In our architecture this is somewhat of a problem, hence we were used to a different behaviour in the world we came from ;).
Now the questions:
Is it possible to forbid object changes outside of a transaction with NHibernate (e.g. throw an exception)?
Is it possible to only commit the changes, that were actually made inside of the current transaction?
Is there some completly different thing, that we could do to enforce that changes to persistent Objects are made only in a particular scope (using-directive, class, namespace, etc)?
Thanks for your thougths, BaSche
I would answer your questions no, no and no. Of course anything is possible, for example you could do something ugly with INotifyPropertyChanged and throw an exception if there's no transaction. But NHibernate is not designed to work as you desire.
NHibernate is designed to allow your objects to be ignorant of the persistence layer and it mostly succeeds in these (with some notable exceptions like requiring virtual for lazy loading). An NHibernate transaction applies to sync'ing object state with the database, not with changes to the objects themselves. Also, you would have to be able to rollback changes to an object if the transaction is aborted and that's challenging.
Object x wasn't change outside of a transaction, because when you open session you create transaction not explicit. When you called transaction.Commit() or when you closed session it was done session.Flush(), and all your changes were saved.

Get rid of UoW's implicitness

NHibernate's Session and EF's ObjectContext are implementations of Unit of Work pattern and suggest similar approaches for change tracking: you retrieve some entities, then modify them somehow and after that call SaveChanges/SubmitChanges/Save/etc.
I don't like the implicitness of this approach. I don't like that entity modification automatically means it will be saved. I would like to explicitly mark entities that should be saved. What are the best ways to achieve this kind of control in NHibernate or EF?
(note: fortunately I've never dealt with EF; my answer is about NH only)
I think your initial assumption is wrong:
entity modification automatically
means it will be saved
that's not true; in order to persist the changes you've made you need to either:
call Session.Flush() yourself
set Session's flush mode to AutoCommit (highly not recommended)
use an ITransaction and commit it (by far the best approach).
unless you do any of the above your changes would not be persisted.
Personally I feel that NH gives me complete control over what goes into my DB.
here's a good article.

Is there a way to use the NHibernate session to figure out if changes need to be written to db?

I'm using NHibernate here with C#. I have a cache of nhibernate objects that have lazily loaded objects inside of those that might have changes written to them. I need a way to determine if there are changes that need to be saved. It's quite a lot of effort to set flags when one little thing changes and also quite annoying to compare a cache with a copy of an original (because of the lazy loading).
Just wondering if there's a way I can use the current session object to know if it has pending changes that are about to be written to the db, this is so that I can have a 'do you want to save' prompt if in fact there are changes. I can't autosave, the customer demands a save button.
NHibernate.ISession exposes an IsDirty() method so you should be able to check that.

Is Unit of Work more efficient if I don't care for transactions etc?

If I have 10 database calls on my web page, and none of them require any transactions etc.
They are simply getting data from the database (reads), should I still use the unit of work class?
Is it because creating a new session per database call is too expensive?
With NHibernate, session factory creation is very expensive (so you'll want to cache the session factory once it's created, probably on the HttpApplication) but session creation is very cheap. In other words, if it keeps your code cleaner, multiple session creations is not necessarily a bad thing. I think the NH documentation says it best:
An ISessionFactory is an
expensive-to-create, threadsafe object
intended to be shared by all
application threads. An ISession is an
inexpensive, non-threadsafe object
that should be used once, for a single
business process, and then discarded.
So, using the UoW pattern is probably not more efficient due to the extra overhead, but it's a good practice and the overhead is probably not going to hurt you. Premature optimization and all that.
Yes, you should use a transaction. From Ayende's blog:
"NHibernate assume that all access to the database is done under a transaction, and strongly discourage any use of the session without a transaction."
For more details, here's a link to his blog posting:
http://ayende.com/Blog/archive/2008/12/28/nh-prof-alerts-use-of-implicit-transactions-is-discouraged.aspx