如何在NHibernate更新之前对dB执行验证



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

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

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

下面的场景是一个边缘情况,但仍然是有效的。

假设您有两个用户在同一个对象上工作,并且dB行是否可以更新取决于特定字段的值。为了使其更具体,假设您有一个Product,为了保持简单,这个Product有"Name"one_answers"QuantityInStock"字段。

假设最初,Product有10项,User1和User2想购买该产品。当两个用户都看到初始表单时,他们被告知库存中有10种这种商品。现在User1买了所有10件商品,而User2去喝咖啡。所以User1的事务没有问题。

然后User2喝完咖啡回来,他相信还有10件存货。所以他想买1件,但他必须阻止这样做,因为没有库存。

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

此外,必须进行两次相同的验证,一次使用DataAnnotation,另一次在更新之前进行,违反了DRY原则。

关键是我觉得我错过了一个技巧,但不知道从哪里开始,该调查什么。

这对我来说是一个简单的问题,但想出一个漂亮优雅的解决方案是另一回事。那么问题是你过去是如何处理这种简单的情况的?我是不是想多了?

您尝试过版本乐观锁定吗?

// 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
    }
}

如果其间没有人更改,则零额外往返,并保证事务的序列化

相关内容

  • 没有找到相关文章

最新更新