宁静的API线程冲突

时间:2018-08-13 14:22:26

标签: c# asp.net-mvc rest asp.net-web-api

我目前有一个购物车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)检查似乎认为该项目不存在(导致线程未完成?)在几行上:

enter image description here

但是应该添加以下内容: enter image description here

有人知道理想的解决方法吗?

1 个答案:

答案 0 :(得分:3)

您在这里有比赛条件。当两个请求彼此紧随其后时,第一个请求可能尚未将其更改提交给DB,而第二个请求则查询了相关项目的存在。

您需要应用一些并发控制来解决此问题。基本上有两种方法可以解决:

  1. 序列化对数据库所做的更改。同样,有两个主要选择:
    1. 大多数RDBMS支持可序列化的隔离级别,用于事务或
    2. 您可以使用一些锁定机制。根据您的应用程序体系结构,这可以在数据库级别(例如表锁定)或应用程序级别(.NET锁定结构)上进行。
  2. 您可以应用试错法(或更准确地说,是对错误进行重试)的方法,即优化并发控制

显然,序列化会对性能产生负面影响(尤其是选项1.1),因此通常首选开放式并发控制,而其他情况则用于特殊情况。

幸运的是,EF内置了对乐观并发处理的支持。所有详细信息都将在this MSDN article中进行讨论。

在这种情况下,您甚至可以使用更简单的方法。您需要在(CartID,SweetID)字段中定义复合的唯一约束。这样做确保不能将任何重复项插入到表中。当检测到此类尝试时,您将获得一个异常,通过捕获该异常,您可以根据自己的要求处理情况。例如。您可以启动更新(但请记住,即使在这种情况下,您也需要进行乐观的并发检查,以确保该过程绝对是失败安全的!)

脚注

实际上,禁用按钮只是掩盖问题。在服务器端,您不能相信JS在客户端上所做的事情,因为它完全不受您的控制。用户可以轻松禁用或修改JS。

更新

再次阅读我的答案,我觉得我应该添加一个结论:

在这种情况下,我认为您最好的办法是

  • 按照我的建议设置唯一约束,但不关心检测到重复插入且
  • 引发的异常。
  • 在提交时禁用客户端上的提交按钮。

这样,即使有人操纵您的JS代码,您也可以确保不会在数据库中存储无效数据。同时,您无需过于复杂化数据持久化逻辑。