Hello,
I use nhibernate as my orm and i am trying to implement something that looks straightforward but has become unneccessarily difficult. It concerns how to implement concurrent updates to an entity where it is expected that the entity will be updated simultaneously by more than one user in a multiuser scenario. The entity is an account of some kind that has a balance.
An example is an inventory system that requires updating a balance of the inventory item based on movement entries in or out. I've included below an shortened version of the code below. The code simply saves the entry and relies on nhibernate to cascade saves all the way to the items.
The system is expected to handle multiple users. The objects are created in the UI over a period of time. Each item's balance will be adjusted from the quantity on hand at the time the entry was created in the UI and the item fetched. It may be several minutes before the user finally saves.
If another user has made other enteries on the same items, the balances that the first user is working with are now invalid. I know that if a save is attempted NHibernate will throw a stale data exception. But this is not what I want since concurrent updates is a neccessity. What I want is that the current balance of the item be incremented or decremented as required by the quantity of the entry. Or that the item be fetched again at the time of update, locked, a recalculation performed and then finally updated, but I don't know how this can be achieved in this scenario and where exactly the code would reside. I would want to avoid it being in the domain object since controlling pessimistic locking etc is an application/persistence concern. It seems this should be a common scenario but I've not seen anything discussing this.
P.S. I have another concern about how the refetch-lock-recalculate-update approach I outlined above in a situation where an entry have many lines. Will seperate lock requests be sent for each item or is there a way to acquire locks for all the items of entry lines in one go.
Any help would be appreciated.
class TestConcurrentUpdates
{
ISession session;
Entry entry;
ItemRepository itemRep;
EntryRepository entryRep;
void testMethod()
{
...
Entry entry = new Entry();
Item item1 = itemRepository.Get(item1ID);
Item item2 = itemRepository.Get(item1ID);
Item item3 = itemRepository.Get(item1ID);
entry.Lines.Add(new EntryLine(this, item1, 10));
entry.Lines.Add(new EntryLine(this, item2, -4));
entry.Lines.Add(new EntryLine(this, item3, 2));
using (ITransaction transaction = session.BeginTransaction())
{
entryRep.Save(entry);
transaction.Commit();
}
}
}
Item
{
string name;
double balance;
IList<EntryLine> entries = new List<EntryLine>();
public double getBalance() { return balance; }
void addEntryLine(EntryLine line)
{
entries.add(line);
balance += line.getQuantity();
}
}
class Entry
{
IList<EntryLine> lines = new List<EntryLine>();
public IList<EntryLine> Lines { get { return lines; } }
}
class EntryLine
{
Entry entry;
double quantity;
Item item;
public EntryLine(Entry entry, Item item, double quantity)
{
this.entry = entry;
this.quantity = quantity;
this.item = item;
item.addEntryLine(this);
}
public double getQuantity()
{
return quantity;
}
}