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

ASP.NET中用户购买商品时如何更新最新库存?数据库未扣减库存

库存不扣减的核心原因

现有代码存在多方面逻辑错误,直接导致扣减流程无法触发:

  • 存储过程功能不匹配:当前SP_UpdateProducts仅包含UPDATE语句,没有任何SELECT返回逻辑,用SqlDataAdapter.Fill执行该存储过程时无法获取到任何商品、购买数量数据,生成的dt为空,后续foreach循环完全不会执行,自然不会触发更新操作。
  • 参数传递时机错误:执行Fill操作前没有给存储过程传递必填的@PID、@Quantity参数,存储过程根本无法正常运行,甚至会直接抛出参数缺失异常。
  • 连接操作顺序颠倒:SqlDataAdapter.Fill执行完成后会自动关闭数据库连接,代码里在ExecuteNonQuery之后才调用con.Open(),此时执行更新命令时连接仍处于关闭状态,就算循环进入也会报连接未打开的错误。
  • 更新逻辑不合理:存储过程直接将库存赋值为传入的计算值,而非基于数据库现有库存做原子扣减,并发场景下会出现库存覆盖、超卖的问题。
  • 缺少原子性校验:先查库存再更新的逻辑没有加事务保障,高并发下会出现读取到旧库存、扣减后库存为负的问题。
修复方案

第一步:调整存储过程逻辑

将原有的直接赋值更新逻辑改为原子化扣减+库存校验,一条SQL完成库存判断和更新,避免并发问题:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[SP_UpdateProducts]
    @PID int,
    @BuyQuantity int, -- 本次需要扣减的购买数量
    @UpdateResult int OUTPUT -- 输出参数:1=扣减成功,0=库存不足
AS
BEGIN
    SET NOCOUNT ON;

    -- 仅当库存足够时才执行扣减,受影响行数为0说明库存不足
    UPDATE tblProducts 
    SET PQuantity = PQuantity - @BuyQuantity
    WHERE PID = @PID AND PQuantity >= @BuyQuantity

    IF @@ROWCOUNT > 0
        SET @UpdateResult = 1
    ELSE
        SET @UpdateResult = 0
END

第二步:重写后端按钮点击逻辑

修正原代码的流程错误,按照参数校验、调用存储过程、根据返回结果跳转的顺序执行:

protected void btnBuyNow_Click(object sender, EventArgs e)
{
    // 先校验登录状态
    if (Session["USERID"] == null || string.IsNullOrEmpty(Session["USERID"]?.ToString()))
    {
        Response.Redirect("Login.aspx");
        return;
    }
    Int32 UserID = Convert.ToInt32(Session["USERID"].ToString());
    int PID = Convert.ToInt32(Request.QueryString["PID"]);
    // 获取本次购买数量,立即购买默认值为1,也可从前端获取用户选择的购买量
    int BuyQty = string.IsNullOrEmpty(Request.QueryString["Qty"]) ? 1 : Convert.ToInt32(Request.QueryString["Qty"]);

    // 校验购买数量合法性
    if (BuyQty <= 0)
    {
        ClientScript.RegisterStartupScript(this.GetType(), "tip", "alert('购买数量不合法');");
        return;
    }

    using (SqlConnection con = new SqlConnection(CS))
    using (SqlCommand cmd = new SqlCommand("SP_UpdateProducts", con) { CommandType = CommandType.StoredProcedure })
    {
        // 执行命令前先传递所有必填参数
        cmd.Parameters.AddWithValue("@PID", PID);
        cmd.Parameters.AddWithValue("@BuyQuantity", BuyQty);
        // 添加输出参数接收扣减结果
        SqlParameter outputParam = new SqlParameter("@UpdateResult", System.Data.SqlDbType.Int)
        {
            Direction = System.Data.ParameterDirection.Output
        };
        cmd.Parameters.Add(outputParam);

        // 显式打开连接后执行命令
        con.Open();
        cmd.ExecuteNonQuery();

        // 根据存储过程返回的结果处理后续逻辑
        int updateResult = Convert.ToInt32(outputParam.Value);
        if (updateResult == 1)
        {
            // 此处需补充订单、订单详情数据的插入逻辑,完成后再跳转确认页
            Response.Redirect($"OrderConfirmation.aspx?PID={PID}&Qty={BuyQty}");
        }
        else
        {
            ClientScript.RegisterStartupScript(this.GetType(), "tip", "alert('商品库存不足,无法购买');");
        }
    }
}
额外注意事项
  • 库存扣减、订单插入、用户支付等关联操作需要包裹在同一个数据库事务中,保证所有操作要么全部成功,要么全部回滚,避免出现扣了库存但没有生成订单的异常。
  • 不建议使用AddWithValue传递参数,最好显式指定参数的数据库类型,避免隐式转换导致的索引失效、参数截断问题。
  • 原代码完全没有补充订单生成的逻辑,库存扣减成功后必须同步写入订单、订单详情表,否则用户购买后没有对应的交易记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 04:54:04