views:

240

answers:

2

I am having a problem with a race condition when saving to the database asynchronously using NHibernate. First an insert to the database is done asynchronously where the unique id is auto-generated. Before this insert returns back to the main thread with now persisted object that has the unique database generated id, the object in updated in some way. The update will fail if I call session.Update because the object to be updated does not have a an id value yet. If I call SaveOrUpdate it will obviously result in an insert instead of an Update because the id field of my entity is equal to the unsaved-value property. Hopefully this code makes the situation more clear:

Entity entity = new Entity()
//update some fields
entity.PropertyTwo = "new value";
//dataObject as the database auto-generated Id
//insert new row asynchronously in different thread
Entity entity.Id = dao.save(entity.Clone()).Id

//before the the entity is returned from the database, the entity might be updated
entity.Property = 'new value';
//entity might be sent without an Id since the first asynch call has not returned yet.
//update asynchronously in another thread
Object dataObject = dao.Update(entity); //fails because Id is not set yet

One solution is to generate the unique id in the code prior to saving. In this case the application manages the incrementing of the unique id as opposed to the database. Any other ways of handling this?

+1  A: 

Looks like you could be creating multiple threads in a single NHibernate Session. Sessions in NHibernate are not thread safe. You should never access the same Session in two concurrent threads. Try creating a separate session in your new thread and see if it resolves your problem.

Check out the NHibernate documentation section 10.2.

Rohit Agarwal
No I am creating one session per thread. The race condition arises if an update is updated on an entity before the insert completes
infinity
keep in mind that each database call occurs on a different thread so if an update is attempted on second thread before the insert completes on a previous thread there will be an error.
infinity
Could you explain why you are cloning the entity object and saving the clone? You may have already tried this, but if you do SaveOrUpdate() in all your threads for the same entity object (no clones), then whichever thread does insert first, the others will do updates. Does this work for your design?
Rohit Agarwal
i am cloning the entity object first because I am sending it to another thread. I believe it is generally recommended not to share objects between threads.
infinity
A: 

You need to put some kind of "Wait for the insert to finish" type logic before you call update. This is standard multi-threaded async programming.

It's very possible to call Insert and Update at the same time on the same object in a multi-threaded environment. You just need to make sure the underlying code is smart enough to:

  1. Do the insert before the update
  2. Wait for the insert to complete before starting the update

There are many different ways to do this and none of them are necessarily better than another. You conceptually need a lock that you can acquire, discard, and wait on.

Also, this problem doesn't really have anything to do with NHibernate. Multithreaded programming is very difficult. It's best to avoid multiple threads if at all possible as complexity can easily spiral out of control.

Michael Maddox