Is letting the DB manage keys rather than NH bad? and why?

66 views
Skip to first unread message

Davec

unread,
Oct 28, 2011, 7:04:21 AM10/28/11
to nhusers
Hi folks,
I'm seeking further clarification on if (and why) letting the DB,
sqlserver for instance manage keys via an auto increment identity is
not recommended over generating the ID on the NH side of things.

I've read webpages like:

http://nhforge.org/blogs/nhibernate/archive/2009/03/20/nhibernate-poid-generators-revealed.aspx
http://ayende.com/blog/3915/nhibernate-avoid-identity-generator-when-possible

and have been recommended to choose guid or guid.comb as the PK type
if working on a greenfield project, which I am. However guids aren't
pretty and we would like to use regular auto increment with sql server
unless there is a good reason not to. We might also have a need to
insert records outside the context of our app so if we would need to
ensure alignment between whatever format of PK NH generates and
whatever format our other processes are using to update the DB.

I'm aware that there is an extra DB hit required to enable NH to get
the ID of an inserted element if we use auto increment as opposed to
NH generated ID. I'm also aware that the links above say that auto
increment breaks the unit of work, however in my tests I have not
found this. Perhaps this is a legacy concern and is not relevent to
3.2 which is what I'm using at the moment.

Clarity on this situation is most welcome? Why not use simple stand
ints autoincrements?

Jason Meckley

unread,
Oct 28, 2011, 8:43:35 AM10/28/11
to nhu...@googlegroups.com
the UoW is broken with identity, but in a subtle way. and in most cases it's not an issue.

your other option, besides guids is HiLo. this uses a long/int for the PK and ensures uniqueness. however there may be gaps in the keys, or the logically order isn't the PK order. but really PK shouldn't mean much outside of the DB, so it shouldn't be an issue. Because NH is not the only way to write data to the database, GUID would be a better choice over HiLo. Otherwise you have to implement the same HiLo algorithm for the other access points.

Identity breaks the UoW concept in NH because of how and when it calls the DB to get the ID. This is best explained by the articles you mentioned. But in a nutshell, NH shouldn't update the database until the transaction is committed (session flushed). try this. run NH without using a transaction or flush and you will see the database still get queried, because NH needs the ID from the database.

You also cannot take advantage of NH's batch writes with identity. This can be a powerful (and easy) performance booster when optimizing the queries and commands used by NH.

Greg Young

unread,
Oct 28, 2011, 9:47:53 AM10/28/11
to nhu...@googlegroups.com

To add to the other responses i might rethink your integrate through the database strategy. There is loads of info on this various places on the internet

--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.

Davec

unread,
Oct 28, 2011, 12:14:29 PM10/28/11
to nhusers
Hi guys, thanks for the responses.

With identity because NH goes to the DB early at the point of
currentsession.save() I had to wrap a try/catch and rollback in the
catch around currentsession.save() as opposed to only having a try/
catch and rollback around currentsession.commit() which would work for
NH managed keys.

So to use identity I just needed to have more rollbacks around more
fails points so yes the problem is small.

cheers.

Jason Meckley

unread,
Oct 28, 2011, 1:27:48 PM10/28/11
to nhu...@googlegroups.com
you wouldn't wrap the commit. you would wrap the entire transaction.

try
   open session
   try
     begin transaction
     do work
     commit transaction
   catch
     rollback transaction
     throw
   finally
    dispose of transaction
finally
 dispose of session

Davec

unread,
Oct 28, 2011, 1:54:21 PM10/28/11
to nhusers
Hi Jason,
Emmm, getting different approaches to this. I'm generally using the
approach defined in scott millets asp.net professional design patterns
book but it only seems to work with NH managed keys. It gets the
session via a static getcurrentsession method which is tied to the
current request via httpcontext items and calls save/saveorupdate/
delete on that via a contrete implementation and then wraps
getcurrentsession.commit in a transaction & rollback. so it has no
wraps around the various currentsession.save/delete etc.

What I'm now doing is something like below and it's working fine with
auto increment PKs.
As an example in a service method i have code like the following

_unitOfWork.BeginTransaction();
..do work on object a
..do work on object b
_unitOfWork.RegisterNew(a);
_unitOfWork.RegisterNew(b);
_unitOfWork.Commit();

_unitofwork looks like this:

public void RegisterAmended(IAggregateRoot entity)
{
try
{

SessionFactory.GetCurrentSession().SaveOrUpdate(entity);
}
catch (Exception ex)
{

SessionFactory.GetCurrentSession().Transaction.Rollback();
throw;
}
}

public void RegisterNew(IAggregateRoot entity)
{
try
{
SessionFactory.GetCurrentSession().Save(entity);
}
catch (Exception ex)
{

SessionFactory.GetCurrentSession().Transaction.Rollback();
throw;
}
}

public void RegisterRemoved(IAggregateRoot entity)
{
try
{
SessionFactory.GetCurrentSession().Delete(entity);
}
catch (Exception ex)
{

SessionFactory.GetCurrentSession().Transaction.Rollback();
throw;
}
}

public void Commit()
{

try
{

SessionFactory.GetCurrentSession().Transaction.Commit();
}
catch (Exception ex)
{

SessionFactory.GetCurrentSession().Transaction.Rollback();
throw;
}

}

public void BeginTransaction()
{
SessionFactory.GetCurrentSession().BeginTransaction();
}

I 'think' if I was using NH managed keys, I would not have to wrap the
various amend calls in transactions because there would be no database
interaction until commit. What you think?

Jason Meckley

unread,
Oct 31, 2011, 8:56:40 AM10/31/11
to nhu...@googlegroups.com
ouch, that is horrendous use of NH and the duplicated exception code is just terrible. I highly recommend searching the web for alternative NH strategies.

with MVC frameworks you can typically reduce the NH footprint within the controller action to 1 call. the session and transaction scope are encapsulated else where

begin request - open session
begin action - begin transaction
action - load entity(s)
end action -  if error rollback, else commit
end request - dispose session

actions can then look like this

public Result GetEntity(long id)
{
   var entity = session.Get<Entity>(id);
   return entity.ToViewModel();
}

public Result ModifyEntity(ViewModel view)
{
   var entity = session.Get<Entity>(view.Id);
   entity.Property = view.Value;
   ...
   //no need to explicitly call session.Save or the like
   return RedirectToAnotherAction();
}

public Result RemoveEntity(ViewModel view)
{
   var entity = session.Get<Entity>(view.Id);
   var child = session.Load<Child>(view.ChildId);
   entity.Remove(child);
   ...
   //no need to explicitly call session.Delete
   return RedirectToAnotherAction();
}

Davec

unread,
Nov 1, 2011, 8:42:08 AM11/1/11
to nhusers
everything in transaction similiar enough to what you suggested
above...

//ismodelvalid
//try
//begin trans
//do a
//do b
//let NH know about changes to a
//let NH know about change to b
//committ
//Catch
//Rollback
//throw up to controller
//finally
//any cleanup

I am using MVC, but we decide to go explicity as opposed to managing
transactions in centralised being_request or simillar.
Cheers.

Jason Meckley

unread,
Nov 1, 2011, 12:37:44 PM11/1/11
to nhu...@googlegroups.com
You shouldn't need to inform NH of changes to the domain model. NH is tracking that automatically.


"I am using MVC, but we decide to go explicity as opposed to managing transactions in centralised"
Not sure I understand why you would introduce complexity and duplicated code, but that doesn't really have any bearing on the topic.

Back too the topic.
There are advantages to allowing the client (NH) to managing the database keys. Most of the advantages become apparent when you begin to performance tune NH is high traffic scenarios. technically speaking identity works, but conceptually it does not align to the unit of work concepts

Davec

unread,
Nov 1, 2011, 1:11:54 PM11/1/11
to nhusers
Thanks Jason,
Yeah with what I was saying, was that we have made a decision to do
explicit transaction rollback etc. handling within each service layer
method as opposed to in being_request and end_request in global.asax,
the lead developer prefairs it this way. If we where to attempt a
session.flush or session.commit in end_request and indeed there was an
error for sure we could rollback and maintain DB integrity however how
would be handle errors, would we not be limited to just redirecting to
a generic error page. I'm planning on rolling back in the service
layer, bubbling up to the calling controller and then bubbling up to
the specific view.

Also I'm not sure what you meaning by not having to tell NHib of
changes? How does it track it automatically? Is that not what
currentsession.save/saveorupdate and currentsession.remove for? To
tell NH that something has changed? Maybe I don't need it for changes
and delete as I most likely get them via load/get as from then on NH
tracks in the session but I do need an explicity call
currentsession.save right for completely new objects? Again if I need
it for some and not others I'd prefare to tell NH explictly about
everything (update/new/remove) because we are all new to NH in here,
so a reference of how things will work in this app is what I'm trying
to put together.

Yeah performance of the extra hit to get the last insert will be a hit
alright, thankfully our app isn't too performance heavy, with only a
few concurrent users. But I've got my hands on NHProfiler recently so
that should help with general performance tuning,

cheers Jason.

Jason Meckley

unread,
Nov 2, 2011, 10:02:09 AM11/2/11
to nhu...@googlegroups.com
if you retrieve and entity from NH it will track changes made to the entity. If you edit a property/field. If you add/remove an object from a collection. if you change a referenced entity. NH will know what you did and update the database records accordingly (insert, update, delete). This also depends on how you map the entities. But in general NH will handle this for you.

The one time you need to explicitly call session.save is when you are adding a new transient object without reference to a persistent object.
in other words
var customer = new Customer{ name= "jason"};
session.Save(customer);

other than that, you typically don't need to explicitly call save, saveorupdate or delete.


"If we where to attempt a session.flush or session.commit in end_request and indeed there was an error for sure we could rollback and maintain DB integrity however how would be handle errors, would we not be limited to just redirecting to a generic error page."
not true. you still have access to the entire request and can decide what to do next should you encounter an exception. In other words handle the exception based on exception type and the context of the request.

typically the exceptions encountered at the point of committing changes should be so negligible that the exception truly is exceptional and the best choice of action is to inform the user with a generic error and to "try again".
verification and validation would have already be preformed within the service layers. these layers would provide the specific context of what to do if the data is not correct.

Davec

unread,
Nov 3, 2011, 9:20:38 AM11/3/11
to nhusers
Hi Jason,
cheers, this all makes sense. about the detached/attached objects.
The exception logic you've outlined makes sense too.

Back to the main topic though, One of our colleagues raised concerns
that identity keys mean you can't use first level cache. I thought
however that NH will hit the db early for updates, and updates are not
cachable, so I'm wondering how first level cache is effected.

My collegues explaination from email was:

"As the first level cache within the session would have to save and
flush new entities to the database and retrieve the newly created Ids
before they could be associated with other objects. More of an issue
when you’re dealing with saving and manipulating a larger graph of
data."

Does NH getting inserting the record 'early' to get the next ID flush
the session and thus the 1st level cache?
Is it not that case that NH gets the id it needs but does not flush
the cache?

Jason Meckley

unread,
Nov 3, 2011, 3:07:07 PM11/3/11
to nhu...@googlegroups.com
1st level cache is not affected by the identity POID. What is lost is the ability to do batched writes.
Reply all
Reply to author
Forward
0 new messages