我目前有一个购物车API,用于在商品不存在时将其添加到表格中,然后在每次添加商品时增加qty列:
var exist = _context.Carts.Any(a => a.CartID == dto.CartSesID && a.SweetID == dto.SweetID);
if (!exist)
{
// Create a new cart item if no cart item exists
var cartItem = new Cart
{
SweetID = dto.SweetID,
CartID = dto.CartSesID,
Qty = qty,
DateCreated = DateTime.Now
};
_context.Carts.Add(cartItem);
}
else
{
var cartItem = _context.Carts.FirstOrDefault(a => a.CartID == dto.CartSesID && a.SweetID == dto.SweetID);
// If the item does exist in the cart,
// then add one to the quantity
if(type == "plus")
cartItem.Qty = cartItem.Qty + qty;
if (type == "minus")
cartItem.Qty = cartItem.Qty - qty;
if(cartItem.Qty == 0)
_context.Carts.Remove(cartItem);
}
// Save changes
_context.SaveChanges();
问题是如果多次按下按钮(如果其他启动时线程未完成?),则if(!exist)检查似乎认为该项目不存在(导致线程未完成?)在几行上:
有人知道理想的解决方法吗?
答案 0 :(得分:3)
您在这里有比赛条件。当两个请求彼此紧随其后时,第一个请求可能尚未将其更改提交给DB,而第二个请求则查询了相关项目的存在。
您需要应用一些并发控制来解决此问题。基本上有两种方法可以解决:
显然,序列化会对性能产生负面影响(尤其是选项1.1),因此通常首选开放式并发控制,而其他情况则用于特殊情况。
幸运的是,EF内置了对乐观并发处理的支持。所有详细信息都将在this MSDN article中进行讨论。
在这种情况下,您甚至可以使用更简单的方法。您需要在(CartID,SweetID)字段中定义复合的唯一约束。这样做确保不能将任何重复项插入到表中。当检测到此类尝试时,您将获得一个异常,通过捕获该异常,您可以根据自己的要求处理情况。例如。您可以启动更新(但请记住,即使在这种情况下,您也需要进行乐观的并发检查,以确保该过程绝对是失败安全的!)
脚注
实际上,禁用按钮只是掩盖问题。在服务器端,您不能相信JS在客户端上所做的事情,因为它完全不受您的控制。用户可以轻松禁用或修改JS。
更新
再次阅读我的答案,我觉得我应该添加一个结论:
在这种情况下,我认为您最好的办法是
这样,即使有人操纵您的JS代码,您也可以确保不会在数据库中存储无效数据。同时,您无需过于复杂化数据持久化逻辑。