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

如何在NHibernate更新之前对dB执行验证,nhibernate,sharp-architecture,Nhibernate,Sharp Architecture,下面的场景很常见,虽然我知道一种解决方法,但缺乏优雅 我举的例子是基于 我正在编写的应用程序是一个ASP.NET MVC应用程序,必须支持多个用户在同一对象上工作 下面的场景是一个边缘案例,但仍然是一个有效的案例 假设有两个用户在同一个对象上工作,dB行是否可以更新取决于特定字段的值。为了使它更具体,假设您有一个产品,并且为了保持简单,这个产品有“Name”和“QuantityInStock”字段 假设最初,产品有10项,User1和User2想要购买该产品。当两个用户都收到初始表单时,他们被告

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

我举的例子是基于

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

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

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

假设最初,产品有10项,User1和User2想要购买该产品。当两个用户都收到初始表单时,他们被告知其中有10个项目在库存中。现在,用户1购买所有10件物品,而用户2则去喝咖啡。因此,User1的事务没有问题

然后User2在喝完咖啡后回来,他相信还有10种商品在库存中。因此,他试图购买1,但必须阻止他这样做,因为没有库存物品

因此,这个问题可以通过使用ASP.NET DataAnnotations验证来解决,这将适用于大多数情况。但是,在我们的edge案例中,假设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
    }
}
public void buything(ProductModel ProductModel,整数金额)
{
int=5;
布尔成功=假;
而(!success&&trys>0)
{

如果(productModel.QuantityInStock)是,这当然有效。但是,情况可能是用户1买5,而用户2买1,在这种情况下,我们不想触发StaleObjectException。为什么不呢,如果不触发StaleObjectException,则数量为5或9,单位为db,但实际上应该是4,否?可能是我的“CreateOrUpdate”代码不够清楚。请在“如果”条件下查看更新以应用更新。想象一个有大量事务的网站。如果有库存但触发StaleObjectException,则用户不会太乐意重新提交订单。