You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC使用EF Core时多次调用控制器方法导致购物车更新异常如何解决

问题诱因

核心原因是快速多次点击触发了并发请求的竞态条件,结合你当前的代码逻辑,具体表现为:

  • ASP.NET Core中每个请求对应独立的EF Core DbContext实例,多个并行请求之间的上下文数据互不感知
  • 两次请求几乎同时查询到相同初始值的CartItem,各自对Quantity做+1计算后,后完成的请求的SaveChanges会直接覆盖先完成请求的修改,导致cartItemPrice看起来只更新了一次
  • CartTotalPrice始终能更新是因为两次请求的AddToTotalPrice方法会分别读取当前最新的总价做累加,不会出现覆盖的情况
可行解决方案

1. 前端先做防重复点击(成本最低,见效最快)

给+号按钮增加点击后立即禁用的逻辑,从入口减少重复请求的产生,示例JS代码:

// 页面加载完成后绑定事件
document.addEventListener('DOMContentLoaded', function() {
  document.querySelectorAll('.add.quantity-btn').forEach(btn => {
    btn.addEventListener('click', function() {
      // 禁用点击,防止重复触发
      this.style.pointerEvents = 'none';
      this.classList.add('disabled');
      // 页面刷新后会自动恢复可点击状态,如果是AJAX请求则在请求回调中恢复
    });
  });
});

2. 后端优化事务逻辑,减少不必要的多次提交

你当前代码中AddToTotalPrice单独调用一次SaveChanges,AddOne最后又调用一次SaveChanges,一个请求两次数据提交,增大了并发冲突的概率,建议将两个修改合并到同一个事务中一次提交:

public async Task AddOne(int id)
{
    // 开启事务,保证所有修改要么全部成功要么全部失败
    using var transaction = await _context.Database.BeginTransactionAsync();
    try
    {
        var cartItem = await _context.CartItem.Include(d => d.Dish).FirstOrDefaultAsync(s => s.Id == id);
        var currentUser = GetCurrentUserName();
        var cart = await _context.Cart.FirstOrDefaultAsync(p => p.UserName == currentUser);

        if (cartItem == null || cartItem.CartId != cart.Id)
        {
            await transaction.RollbackAsync();
            return;
        }

        cartItem.Quantity += 1;
        cartItem.cartItemPrice = cartItem.Dish.Price * cartItem.Quantity;
        cart.CartTotalPrice += cartItem.Dish.Price;
        
        // 一次保存所有修改,不需要单独的AddToTotalPrice方法
        await _context.SaveChangesAsync();
        await transaction.CommitAsync();
    }
    catch
    {
        await transaction.RollbackAsync();
        throw;
    }
}

3. 增加乐观锁避免修改覆盖

给CartItem实体增加行版本字段作为并发校验标记,EF Core检测到并发冲突时会自动抛出异常,避免修改覆盖,示例配置:

// CartItem实体类增加行版本字段
public class CartItem
{
    // 其他原有字段
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

配置后如果出现并发修改,EF Core会抛出DbUpdateConcurrencyException异常,你可以捕获该异常后做重试或者提示用户操作失败。

内容的提问来源于stack exchange,提问作者user91489

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:09:02