私は自分の orm として nhibernate を使用しています。単純に見えるものを実装しようとしていますが、不必要に難しくなっています。これは、エンティティがマルチユーザー シナリオで複数のユーザーによって同時に更新されることが予想される場合に、エンティティに同時更新を実装する方法に関するものです。エンティティは、残高のあるある種のアカウントです。
例としては、入出庫の移動に基づいて在庫品目の残高を更新する必要がある在庫システムがあります。以下のコードの短縮版を以下に含めました。このコードは単純にエントリを保存し、nhibernate に依存して保存を項目までカスケードします。
システムは複数のユーザーを処理することが期待されています。オブジェクトは一定期間にわたって UI で作成されます。各アイテムの残高は、UI でエントリが作成され、アイテムが取得された時点の手持ちの数量から調整されます。ユーザーが最終的に保存するまでに数分かかる場合があります。
別のユーザーが同じアイテムに対して別の入力を行った場合、最初のユーザーが使用している残高は無効になります。保存を試みると、NHibernate が古いデータの例外をスローすることはわかっています。しかし、同時更新が必要なので、これは私が望むものではありません。私が欲しいのは、アイテムの現在の残高が、エントリの数量によって必要に応じて増加または減少することです。または、更新時にアイテムが再度フェッチされ、ロックされ、再計算が実行されてから最終的に更新されますが、このシナリオでこれをどのように実現できるか、コードが正確にどこに存在するかはわかりません。悲観的ロックなどを制御することはアプリケーション/永続性の問題であるため、ドメインオブジェクトに入らないようにしたいと思います。これは一般的なシナリオのようですが、これについて議論しているものは見たことがありません。
PSエントリに多くの行がある状況で、上で概説したrefetch-lock-recalculate-updateアプローチがどのようになるかについて、別の懸念があります。アイテムごとに個別のロック要求が送信されますか、またはエントリ行のすべてのアイテムのロックを一度に取得する方法はありますか?
どんな助けでも大歓迎です。
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;
}
}