Thanks for the quick reply.
I get that you need to tell NHibernate to make the object persistent with Save method which makes sence.
What we want to do is create a unit of work and anything added to the unit of work will be inserted or updated, if not explicitly added to the unit of work it will should be discared during commit. I thought this was the role of the session and my assumption was that the update method marked the object as dirty (or potentially dirty) and SaveOrUpdate was simply if IsTransient Save() else Update() as the method name implies.
Forgive my ignorance, but the actual behavior is significantly different than my expectations, with several potentially problematic ramifications. It appears I have completely misunderstood the role of the session and the update method. I'm no longer entirely sure what the correct usage of NHibernate is within a .net web application.
We currently open the session and transaction on begin request and commit the transaction on endrequest unless an error occured. This allows view commonents and helper methods to render navigation, kpi, and other content using the context bound session. Although navigation items are not usually modified by a request, some pages do allow users to modify navigation items so so we can't simply say load all items except what we intend to modify as readonly because the repository loading the data currently doesn't know or care if the data will be need to be modified.
This most straight forward might seem to be "don't modify the object then." However this does't work well either as a page might make multiple modifications. Several events are initiated during a post which may are processed by different listeners to determine if a change needs to be made to the object, in some cases one object might be modified but enter an invalid state so althought the memory version of the object was modified the object should NOT saved, but this is a graceful failure in which the user will be notified and/or the object added to an error queue. The transaction itself is not rolledback and other modifications may still be made. However what you are saying seems to indicate that I must rollback the transaction or evict the object.
This could potentially be a serious issue because if what your saying is by design the object will still be saved even though it the object is in an invalid state, nor can the transaction be rolled back (in some cases) because other modifications will be made. So now I'm not entirely sure what the best practice is?
I can partly understand why it works the way that it does since NHibernate returns the same instance an updated object that is not evicted could be queried again later returning the dirty object rather than a clean object, so evicting it seems like a good idea but if I were implementing this with POCO I would have the repository return a copy of the stored data and once I'm satisfied with my modifications I call Save updating the persisted object (perhaps this is the purpose of StatelessSesison).
There are a few potential solutions I can think of:
1) Perform all modifications within a try { .. if(valid) Save(obj) else Evict(obj) } catch { Evict(obj) }. I have no idea if evict cascades and would require us to manually track clean objects for eviction rather than dirty objects which seems complete backwards and not even sure how to determine which object may have been lazy loaded.
2) I discovered ILifecycle interface last night which could allow us to implement a our own Unit of Work by returning Veto OnSave unless we called Save explicitly. This seems like it might be doable. The potential issue here is that we would loose the implicit cascade functionality. The other issue is that since NHibernate returns the same object reference an object which was loaded, modfied but not saved could be loaded again later (and assumed clean) but was already invalid.
3) StatelessSesison. Sounded like the stateless session was intended for bulk loading so I didn't pay much attention to it. Session without cache I guess. But since part of the problem seems to be that NHibernate returns same reference rather than copy of object until save perhaps stateless session behaves more like I was expecting the session to behave.
4) Open readonly (Flush.Never should be readonly right?) session in BeginRequest to allow view components, helpers, etc to render whatever. The event listeners however would open a sperate session and transaction. The concern here is that we need to ensure all entities loaded from the readonly session loaded into the new session. This might have advantages over 3 since it would support caching and obect reference equality tests. It seems that SaveOrUpdateCopy might be my lifesaver here and I'm thinking this might be the best approach/recommended practice?
Thoughts?
Thanks,
Kurt
|