1

下面的场景是,我会说很常见,虽然我知道解决它的一种方法,但它缺乏优雅。

我给出的示例基于https://github.com/sharparchitecture/Sharp-Architecture-Cookbook

我正在编写的应用程序是一个 ASP.NET MVC 应用程序,并且必须支持多个用户在同一个对象上工作。

以下场景是一个边缘案例,但仍然是一个有效的案例。

假设您有两个用户在处理同一个对象,并且 dB 行是否可以更新取决于特定字段的值。为了使其更具体,假设您有一个产品,并且为了简单起见,该产品具有“名称”和“数量库存”字段。

假设最初有 10 件产品,用户 1 和用户 2 想购买该产品。当两个用户都看到初始表格时,他们被告知有 10 件此类物品有库存。现在用户 1 购买了所有 10 件商品,而用户 2 去喝咖啡。所以User1的交易没有问题。

然后,用户 2 喝完咖啡回来,相信还有 10 件商品有库存。因此他尝试购买 1,但必须阻止他这样做,因为没有库存商品。

所以这个问题可以通过使用 ASP.NET DataAnnotations 验证来解决,这将捕获大多数情况。然而,在我们的边缘案例中,假设 User1 和 User2 在几分之一秒内执行相同的操作,这样当 User2 提交表单时,它通过了 ASP.NET 验证,但当它到达持久层时,数量库存为 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 而不必将数据从一个对象传输到另一个对象。

此外,必须进行两次相同的验证,一次使用 DataAnnotation,另一次在更新之前违反 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 回答