2

重要なトランザクションを処理するアクションがあり、トランザクションを処理する最善の方法がわかりません。

これは私がしなければならないことの簡単な例です:

[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
    // Not sure what isolation level I sould use here to start with...
    IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
    using(new TransactionScope(isolationLevel)){

        // Retreive the order
        var order = GetExistingOrder(orderKey);

        // Validate that the order can be processed
        var validationResult = ValidateOrder(order);

        if (!validationResult.Successful)
        {
            // Order cannot be processed, returning
            return View("ErrorOpeningOrder");
        }

        // Important stuff going on here, but I must be sure it 
        // will never be called twice for the same order
        BeginOrderProcess(order);

        return View("OrderedProcessedSuccessfully");
    }
}

最初に質問したいのは、この種の操作では、同じ注文に対して同時に複数のリクエストを行うことができる場合 (つまり、同じ注文に対するブラウザーからのクイック リクエスト)、悲観的ロックを使用して 1 つのトランザクションを実際に保証する必要があるかどうかです。時間またはBeginOrderProcess楽観的ロックを使用してほぼ同時に同じ注文に対する2つの同時リクエストで2回呼び出されないようにする方法があります(おそらくより高速になると考えられます)?

第二に、私はそれを完全に間違った方法で行っていますか?このようなケースを処理するためのより良い方法はありますか? 言い換えれば、これをどのように処理すればよいですか?:)

4

1 に答える 1

1

わかりました、いくつかの調査の後、私は私が欲しかったものを見つけたと思います.

このような場合、悲観的ロックを nhibernate で (を使用してsession.Lock(order))使用するのはやり過ぎです。

以前は使い方がわからなかったという理由だけで楽観的ロックを選択しました。

コードは次のようになります。

[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
    // I confirm, here I really want ReadCommit since I need optimistic lock
    IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
    using(var tx = new TransactionScope(isolationLevel)){

        // Retreive the order
        var order = GetExistingOrder(orderKey);

        // Validate that the order can be processed
        var validationResult = ValidateOrder(order);

        if (!validationResult.Successful)
        {
            // Order cannot be processed, returning
            return View("ErrorOpeningOrder");
        }

        // Important stuff going on here, but I must be sure it 
        // will never be called twice for the same order
        BeginOrderProcess(order);

        // The main difference is here
        // I need to do an explicit commit here to catch the stale object exception
        // and handle it properly. Before that, 
        // I was handling the commit in the dispose of my TransactionScope
        // Since my transaction scope is in ReadCommit, no one but this request 
        // should be able to read the modified data whatever the changes are
        try{
            try{
                tx.Commit();
            }catch(Exception){
                tx.RollBack();
                throw;
            }            
        }catch(StaleObjectStateException){
            return View("OrderIsCurrentlyBeeingProcessedBySomeoneElse");
        }

        return View("OrderedProcessedSuccessfully");
    }
}

私のコメントが示すように、主な違いは、コミットを手動で処理してから例外を処理することです。この実装により、他のユーザーのリクエストをブロックする心配がなくなり、必要に応じて例外を処理できます。

流暢な nhibernate を使用しており、マッピングでバージョンを使用するようにエンティティを構成しました。

OptimisticLock.Version();

Version(x => x.Version)
.Column("EntityVersion")
.Generated.Never()
.Default(0)
.UnsavedValue("null");

これにより、コミットを行っているときに、NHibernate はバージョンを確認しStaleObjectStateException、コミットが正しいバージョンと一致しない場合にスローします。

ハッピー NHibernating :)

于 2014-10-16T15:48:06.233 に答える