如何在 NHibernate 更新之前针对 dB 进行验证
我想说,以下情况很常见,虽然我知道解决它的一种方法,但它缺乏优雅。
我给出的示例基于 https://github.com/sharparchitecture/Sharp-Architecture-食谱。
我正在编码的应用程序是一个 ASP.NET MVC 应用程序,并且必须支持多个用户处理同一对象。
以下场景是一种边缘情况,但仍然是有效的。
假设您有两个用户在处理同一个对象,并且 dB 行是否可以更新取决于特定字段的值。为了更具体,假设您有一个产品,为了简单起见,该产品具有“名称”和“QuantityInStock”字段。
假设最初有 10 件产品,用户 1 和用户 2 想要购买该产品。当向两个用户提供初始表格时,他们被告知这些商品有 10 件库存。现在,用户 1 购买了全部 10 件商品,而用户 2 去喝咖啡。所以User1的交易没有问题。
然后,用户 2 喝完咖啡回来,相信还有 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 原则。
关键是我觉得我错过了一个技巧,但不太知道从哪里开始以及调查什么。
这对我来说是一个简单的问题,但提出一个漂亮优雅的解决方案是另一回事。那么问题是你过去是如何处理这个简单的案例的?我是不是想太多了?
The following scenario is, I would say quite common and although I know one way of resolving it but it lack elegance.
The example I'm giving is based upon https://github.com/sharparchitecture/Sharp-Architecture-Cookbook.
The application I'm coding is an ASP.NET MVC application and has to support multiple users working on the same object.
The following scenario is an edge case but nevertheless a valid one.
Say you have two users working on the same object and whether the dB row can be updated depends upon the value of a particular field. To make it more concrete, let's say you have a Product and to keep things simple, this Product has "Name" and "QuantityInStock" fields.
Say that initially, there are 10 items of the Product and User1 and User2 want to buy this product. When both users are presented the initial form they are told that there are 10 of these items in stock. Now User1 buys all 10 items while User2 goes to have a coffee. So User1's transaction goes through no problem.
Then User2 comes back after his coffee in the belief that there are still 10 items in stock. So he tries to buy 1 but he must be prevented from doing so since there are no items in stock.
So this problem can be solved by using ASP.NET DataAnnotations validation and this will catch the majority of cases. However, in our edge case, say that User1 and User2 perform the same operation but within a fraction of a second such that when User2 submits the form, it passes the ASP.NET Validation but by the time it gets to the persistence layer, the QuantityInStock is 0.
The solution to this is to perform the validation at the latest moment as possible i.e. just before calling the Update method.
Now for some code.
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
}
}
Now, the fact that I'm calling:
var currentProductModel = Get(productModel.Id);
means that if I were to just do this:
this.productModelRepository.SaveOrUpdate( productModel );
would cause an exception:
a different object with the same identifier value was already associated with the session: 1
Hence, I have to copy all of the values from productModel to currentProductModel. It's fine when using something like Automapper but still kind of feels wrong to me in the sense that I feel I should just be able to save productModel as is without having to transfer the data from one object to another.
Moreover, having to do the same validation twice, once using DataAnnotation and another time just before updating violates the DRY principle.
The point is that I feel like I'm missing a trick but don't quite know where to start and what to investigate.
This to me is a simple problem but coming up with a nice elegant solution is something else. So the question is how have you dealt with this simple case in the past? Am I overthinking this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您尝试过使用版本进行乐观锁定吗?
更新:我以另一种方式处理此类事情,
如果中间没有人更改,则零额外往返,并保证交易的序列化
have you tried optimistic Locking with Version?
Update: i handled such things in another way
zero extra roundtrips if nobody changed it in between, and guaranteed serialisation of the transactions