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

时间:2011-11-30 15:05:35

标签: nhibernate sharp-architecture

以下情况是,我会说非常普遍,虽然我知道解决它的一种方法,但它缺乏优雅。

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

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

以下情况是边缘情况,但仍然是有效情况。

假设您有两个用户在同一个对象上工作,并且dB行是否可以更新取决于特定字段的值。为了使它更具体,让我们说你有一个产品,为了简单起见,这个产品有"名称"和" QuantityInStock"领域。

首先说,产品有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原则。

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

这对我来说是一个简单的问题,但提出一个很好的优雅解决方案是别的。那么问题是你过去如何处理这个简单的案例?我是否想过这个?

1 个答案:

答案 0 :(得分: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
    }
}
如果没有人在其间进行任何更改,那么

零额外往返,并保证事务的序列化