Session Cache isn't working the way I thought it did.

5 views
Skip to first unread message

Josh Rogers

unread,
Jul 1, 2009, 3:36:30 PM7/1/09
to nhu...@googlegroups.com
I don't know if cache is the right terminology, but it is the best I could think of.

I am adding an object to a conversation by calling SessionFactory.GetCurrentSession().Save (which works), but later I need to retrieve that object but I do not know the id  however I do know certain attributes of the object. So I created an ICriteria to look for those particular attributes, which works on its own for persisted objects.  Now my problem is that when I have added the object to the cache but have not flushed it and I later call the attribute search function it never finds the object so it tries to create it again and then it crashes because it is already there.  So the Save call sees the previously created entity as part of the conversation even if it has not been persisted to the DB however the CreateCriteria does not....this is not how I understood it to work, could someone please clarify?

Thanks,
Josh

Fabio Maulo

unread,
Jul 1, 2009, 3:45:11 PM7/1/09
to nhu...@googlegroups.com
Query mean always "query-to-db", after the query to DB is executed the session-cache begin working returning you the existing instance in the session-cache or attaching new instance.
Btw because you are talking about "conversation" we must know the POID strategy you are using.

2009/7/1 Josh Rogers <joshl...@gmail.com>



--
Fabio Maulo

Josh Rogers

unread,
Jul 1, 2009, 3:59:52 PM7/1/09
to nhu...@googlegroups.com
Ok, but how do I get it to look at the cache first?  I don't want it to query the db until it can't find the object in cache, that would seem the natural order of things.

As far as the POID strategy I am using the db generated ID that increments by 1 each time a new item is added.


JOsh

Fabio Maulo

unread,
Jul 1, 2009, 4:31:44 PM7/1/09
to nhu...@googlegroups.com
2009/7/1 Josh Rogers <joshl...@gmail.com>


As far as the POID strategy I am using the db generated ID that increments by 1 each time a new item is added.

If you mean identity you must know how identity work.
Identity break the UnitOfWork pattern and session.Save mean a INSERT to DB.
If you want work with conversation you must choose another POID strategy.

--
Fabio Maulo

Josh Rogers

unread,
Jul 1, 2009, 5:31:17 PM7/1/09
to nhu...@googlegroups.com
Ok, well I have spent the last 4 weeks trying to bend my application to work with NHibernate and I can't lose anymore productivity so I am going to have to pull out NHibernate and go back to dealing with ADO directly or find another ORM solution.

I just wanted to thank everyone for their time, it was greatly appreciated and I truly did learn a lot of new things.

Thank you,
Josh

Graham Bunce

unread,
Jul 1, 2009, 6:36:11 PM7/1/09
to nhusers
Understandable but many of the issues you get with NH (using
Identities generated by a DB for example) you can run into with other
ORM's or ORM-like products such as Linq-SQL. Fabio (quite
understandably) has a thing against DB identity and will usually be
quite aggressive against it and not always explain himself fully for
someone struggling with new concepts. He has an awful lot to do though
and English isn't his first language so we're patient with him :)

NH is a big learning curve IMO but when you finally get your head
around it, it's usually worth it. Maybe consider it for your next
project taking into account what you have learnt from this one. One of
the things you quickly do is to dump DB identity and use HiLo or GUID
COMB - i.e. let NH generate your keys for you.

And to explain your issue, NH always goes to the DB for a query. I
suspect this is for performance reasons as how can it search its cache
on any number of unknown (by the NH designer) parameters that a
developer will provide? It would just be too slow. Therefore it will
ONLY go to the cache where you provide an ID via a non-criteria call
(e.g. Load). It's the only way that I can see it performing. Also, who
is to say that an object you search on by (e.g. name) is the same
object as you added to the cache previously. You may *know* its the
same because you understand the logic, but NH does not. Therefore it
needs to go to the DB to ensure it has the latest view of the DB world
- which ultimately is what NH is all about.

I'm a little hazy as its been a while since I used DB identity with NH
but I think that in your case, you will not get an ID on the save as
you have told NH to get the key from the DB. As you have worked out, a
save does not save to the DB but saves to the cache (to be precise,
adds it to the current Unit of Work). NH will only get the ID when you
Flush the cache (or alternatively I think it will also do it when you
Save within a transaction).

Can you flush after the save or something similar? If not then, tbh,
it sounds like you're not really using NH effectively and need to make
compromises as a result. Just like you'd have to to use Linq-SQL
(inherit from special classes) or ADO.NET (no in-built ORM mapping at
all).

Good luck.

Fabio Maulo

unread,
Jul 1, 2009, 7:01:09 PM7/1/09
to nhu...@googlegroups.com
2009/7/1 Graham Bunce <graha...@hotmail.com>


And to explain your issue, NH always goes to the DB for a query. I
suspect this is for performance reasons as how can it search its cache
on any number of unknown (by the NH designer) parameters that a
developer will provide? It would just be too slow. Therefore it will
ONLY go to the cache where you provide an ID via a non-criteria call
(e.g. Load). It's the only way that I can see it performing. Also, who
is to say that an object you search on by (e.g. name) is the same
object as you added to the cache previously. You may *know* its the
same because you understand the logic, but NH does not. Therefore it
needs to go to the DB to ensure it has the latest view of the DB world
- which ultimately is what NH is all about.

You can even explain the "logic" to NH using <natural-id> ;)

-- 
Fabio Maulo
I prefer to try something good and fail, than to try something wrong and achieve it

Graham Bunce

unread,
Jul 1, 2009, 7:23:25 PM7/1/09
to nhusers
>You can even explain the "logic" to NH using <natural-id> ;)

Sheesh, I've just about got my head around NH 2.0.1GA without getting
my head around all the new 2.1.0 features!!

Fabio Maulo

unread,
Jul 1, 2009, 8:06:43 PM7/1/09
to nhu...@googlegroups.com
2009/7/1 Graham Bunce <graha...@hotmail.com>


>You can even explain the "logic" to NH using <natural-id> ;)

Sheesh, I've just about got my head around NH 2.0.1GA without getting
my head around all the new 2.1.0 features!!

Stay updated... stay updated the world of Open Source is going fast. ;)
--
Fabio Maulo

jobsamuel

unread,
Jul 2, 2009, 12:34:23 AM7/2/09
to nhusers
I thought that since you are using db generated identity, your save
would result in a db call and your further call to criteria query
would work. Even if you are not using db generated id the session
would automatically flush upon a query and your criteria query should
still work. I think this is mentioned in the document that in flush
mode auto firing a query happens to be a flush point.
Is my understanding wrong, if it correct then your criteria query
should have worked, what is the reason for it to fail?

John Rayner

unread,
Jul 2, 2009, 9:12:34 AM7/2/09
to nhusers
Josh,

Some code and a copy of the exception message would allow everyone to
help you better.

Cheers,
John

Josh Rogers

unread,
Jul 2, 2009, 9:35:50 AM7/2/09
to nhu...@googlegroups.com
For future reference I would like to know how I could have handled this situation using NHibernate, so if you'll bear with me I'll give you a brief synopsis of what I am trying to achieve.  This is a transportation/logistics application.

User creates or opens a job
A job for these purposes contains 3 children a shipper, a customer, and a list of details.
Each one of those details contain 2 children a consignee and a weight factor
User will add multiple weight factors and multiple job details utilizing those different weight factors
All of this is done in memory and no changes are persisted to the database until the save button is pressed.  The reasons behind this are plenty, but the most weighing issue is that when a job is being created there are a huge number of changes made and when you consider that a single job could possibly contain close to a thousand points it is much more responsive to work with an in memory model rather than working directly with the DB.

So I was trying to use NHibernates cache to do this rather than storing everything in data tables as I did before.  I understand where the identity issue came into play; I actually dealt with the same issue when I was dealing with data tables directly.  I got around this limitation by using an arbitrary non persisted property of these objects to indicate whether the identity is a valid identity or not by simply using a bool that is called IsNew. 

There were two things I was trying to achieve in this process with NHibernate that I could not due to my lack of knowledge I assume.  First was the issue that my weight factors are unique, there are no duplicates in the db, however I don't get a list of weight factors the user manually types them in each time, searching through the list would be awfully cumbersome.  So since everything is in memory I don't know if it is a duplicate until the save process occurs, this is actually done in a stored procedure, the sproc checks to see if it is a duplicate if it is it returns the identity of the duplicate otherwise it adds a new one and returns that identity.  As far as I know NHibernate doesn't do this automagically so it would have to be done through coding this process but I found that I was getting an object null reference exception when it was trying to create a duplicate weight factor.  That took me forever to find out because the exception reporting in no way indicated that was the issue.  If I could have saved any time in the learning process of NHibernate it would have been if the exceptions were a bit more informative, but this could be because of an issue that is beyond my comprehension.

Second is that I needed everything to be an in memory representation and when the acceptall or a save -> acceptall (for new jobs) was done it would just persist everything to the database allowing me to get rid of large chunks of code that parse data tables to get the information that it needs from them and persist changed and new information to the db.

I was not able to achieve either of these issues.  My only way I know to get around them is still to employ the use of data tables and parse them again during the save process and create the objects for NHibernate during this process.  If that is the case NHibernate does not provide me an increase in development productivity and in fact with the use of IoC now, which was required for the session handling that I needed, it has slowed me down extensively because I am having to essentially redesign my app to use all of these new interfaces that I was not using before.  I understand this would be greatly negated when I finally finish this conversion, but until then it has been a huge drain on my development time and debugging is much more complicated now since I am programming against interfaces so I have to set up a debug points all throughout the code rather than just being able to step through.

When I decided to make the leap to using an ORM in my system I never felt I should have to change my application to work around an ORM that an ORM, by its nature that it is a tool (i.e. you wouldn't change how you built a house because you bought a new hammer), should be flexible and adaptive to my application.  This has proven to be incorrect in the case of NHibernate, or at least in my use of it and the answers to the questions I have asked here seem to indicate that I am correct in my conclusion that NHibernate isn't as adaptive as I need it to be.

Now I know that may anger some of the developers here, and please do not let it.  I don't claim to be god's gift to programming and the way I do things may not be orthodox, but it has carried me in my career thus far.  I think NHibernate is a great tool despite all of my troubles, and had I been working in a web app rather than a win forms app I would definitely use it. 

I do think there should be more thought into handling the types of issues that NHibernate has with Identity as there are a lot of developers out there that use it and transitioning an already existing database to guids would be a nightmare if it is of any size at all, and once again they shouldn't have to do that to utilize a tool, that tool should be adaptive.  Also if at all possible more meaningful exceptions would be of great benefit and would lessen the learning curve dramatically, IMHO.

Anyways, if any of you read to this far, thank you for your time and I REALLY look forward to responses.

Thanks,
Josh

Josh Rogers

unread,
Jul 2, 2009, 9:46:58 AM7/2/09
to nhu...@googlegroups.com
John,

I would love to do that, but I don't know what code would be helpful here.  All of it is quite trivial really.  As far as the exception it is also quite uninformative, here it is:

System.NullReferenceException was caught
  Message="Object reference not set to an instance of an object."
  Source="uNhAddIns.Adapters.Common"
  StackTrace:
       at uNhAddIns.Adapters.Common.AbstractConversationInterceptor.DisposeConversationOnException() in c:\Documents and Settings\HP_Administrator\Desktop\uNHAddins\uNhAddIns\uNhAddIns.Adapters.Common\AbstractConversationInterceptor.cs:line 156
       at uNhAddIns.CastleAdapters.AutomaticConversationManagement.ConversationInterceptor.Intercept(IInvocation invocation) in c:\Documents and Settings\HP_Administrator\Desktop\uNHAddins\uNhAddIns\uNhAddIns.CastleAdapters\AutomaticConversationManagement\ConversationInterceptor.cs:line 46
       at Castle.DynamicProxy.AbstractInvocation.Proceed()
       at IJobDalProxy3e9a670be6024191b1818851699434af.AcceptAll()
       at TEAMSLogic.Impl.JobLogic.AcceptAll() in C:\Source\TEAMS\TEAMS\TEAMSLogic\Impl\JobLogic.cs:line 321
       at TEAMS.Manager_Forms.frmJobManager.btnSave_Click(Object sender, EventArgs e) in C:\Source\TEAMS\TEAMS\TEAMS\Manager_Forms\frmJobManager.cs:line 382


I have been able to reproduce this error using unit tests if I try and do an accept all and one of the weight factors in my job details is a duplicate of another in the db.  Unfortunately the way I have to do things I don't know about the duplicate until the save process and for NHibernate that is too late.  However I also get this error when I use a non duplicate weight factor, but I can't reproduce that in unit tests, and I have simply ran out of time.  Either way this exception is entirely non informative, at least to me anyways.

Thanks,
Josh
Reply all
Reply to author
Forward
0 new messages