1

次のシナリオは、非常に一般的であり、それを解決する方法は 1 つ知っていますが、洗練されていません。

私が提供している例は、 https://github.com/sharparchitecture/Sharp-Architecture-Cookbookに基づいています。

私がコーディングしているアプリケーションは ASP.NET MVC アプリケーションであり、同じオブジェクトで作業する複数のユーザーをサポートする必要があります。

次のシナリオはエッジ ケースですが、有効なシナリオです。

同じオブジェクトで作業している 2 人のユーザーがいて、dB 行を更新できるかどうかは、特定のフィールドの値に依存するとします。より具体的にするために、製品があり、物事を単純にするために、この製品には「Name」フィールドと「QuantityInStock」フィールドがあるとします。

最初に、Product のアイテムが 10 個あり、User1 と User2 がこの製品を購入したいとします。両方のユーザーに最初のフォームが表示されると、これらのアイテムの在庫が 10 個あることが通知されます。ここで、User1 は 10 品目すべてを購入し、User2 はコーヒーを飲みに行きます。したがって、User1 のトランザクションは問題なく実行されます。

その後、User2 は、まだ 10 個のアイテムが在庫にあると考えて、コーヒーを飲んだ後に戻ってきました。そこで彼は 1 を買おうとしましたが、商品の在庫がないため、それを阻止しなければなりません。

したがって、この問題は ASP.NET DataAnnotations 検証を使用することで解決でき、これによりほとんどのケースがキャッチされます。ただし、このエッジ ケースでは、User1 と User2 は同じ操作を実行しますが、User2 がフォームを送信すると ASP.NET 検証に合格しますが、永続化レイヤーに到達するまでに数分の 1 秒以内に実行するとします。 QuantityInStock は 0 です。

これに対する解決策は、可能な限り最新の時点、つまり Update メソッドを呼び出す直前に検証を実行することです。

次に、いくつかのコードについて説明します。

public ProductModel CreateOrUpdate(ProductModel productModel)
{
    var currentProductModel = Get(productModel.Id);

    var currentQuantityInStock = currentProductModel.QuantityInStock;


    if(currentProductModel.QuantityInStock !=0 && productModel.QuantityInStock >= currentQuantityInStock )
    {
        currentProductModel.QuantityInStock= productModel.QuantityInStock;
        currentProductModel.Name = productModel.Name;

        this.productModelRepository.SaveOrUpdate( currentProductModel );
        return productModel;
    }
    else
    {
        //Raise an exception
    }
}

今、私が呼んでいるという事実:

 var currentProductModel = Get(productModel.Id);

つまり、これを行うだけの場合:

 this.productModelRepository.SaveOrUpdate( productModel );

例外が発生します:

同じ識別子値を持つ別のオブジェクトが、すでにセッションに関連付けられています: 1

したがって、すべての値を productModel から currentProductModel にコピーする必要があります。Automapper のようなものを使用する場合は問題ありませんが、あるオブジェクトから別のオブジェクトにデータを転送することなく、productModel をそのまま保存できるはずだと感じるという意味で、私にはまだ少し気分が悪いです。

さらに、1 回は DataAnnotation を使用し、もう 1 回は更新の直前に、同じ検証を 2 回行う必要があるため、DRY 原則に違反します。

要点は、トリックを見逃しているように感じますが、どこから始めて何を調査すればよいかよくわかりません。

私にとってこれは単純な問題ですが、素敵でエレガントな解決策を考え出すことは別のことです。問題は、過去にこの単純なケースにどのように対処したかです。私はこれを考えすぎていますか?

4

1 に答える 1

0

バージョンによる楽観的ロックを試しましたか?

// Fluent mapping
public EntitiyMap()
{
    OptimisticLock.All();   // all properties musn't be changed in db when saving
    // or
    OptimisticLock.Dirty();   // only dirty properties musn't be changed in db when saving
}


//
public ProductModel CreateOrUpdate(ProductModel productModel)
{
    try
    {
        // productModel is already validated and updated
        this.productModelRepository.SaveOrUpdate( productModel );

        return productModel;
    }
    catch (StaleObjectException)
    {
        // somebody changed the object in database after we have read it
        // Raise an exception or whatever
    }
}

更新:私はそのようなことを別の方法で処理しました

public void BuySomething(ProductModel productModel, int amount)
{
    int tries = 5;
    bool success = false;
    while(!success && tries > 0)
    {
        if (productModel.QuantityInStock <= amount)
        {
            //Raise an exception
        }

        productModel.QuantityInStock - amount;
        try
        {
            this.productModelRepository.SaveOrUpdate( productModel );
        }
        catch (StaleObjectException)
        {
            // somebody changed the object in database after we have read it
            this.productModelRepository.Refresh(productModel);
            tries--;
        }
    }
    if (tries <= 0)
    {
        // Raise an exception or whatever
    }
}

誰もそれを変更しなかった場合、余分なラウンドトリップはゼロで、トランザクションのシリアライゼーションが保証されます

于 2011-11-30T17:14:25.643 に答える