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

SQL Server行锁定的正确实现——类ERP WPF应用场景

处理ERP订单场景的SQL Server行锁定方案(WPF应用)

嘿,针对你在WPF类ERP应用中需要锁定订单记录直到用户操作完成的需求,我结合SQL Server的特性和WPF的实际场景,给你梳理几个实用的方案和注意事项:

一、核心思路:用事务+锁定提示实现悲观锁

你的思路完全正确,事务是实现行锁定的基础,但要搭配合适的表提示,才能确保选中的记录在操作完成前不被其他用户修改或读取(根据你的需求)。

1. 关键锁定提示选择

SQL Server提供了几种表提示来控制行锁定行为,针对你的场景推荐两种:

  • UPDLOCK, HOLDLOCK:获取更新锁并保持到事务结束,其他用户可以读取该记录(但无法加更新锁或排他锁,也就无法修改)。适合允许其他用户查看但不允许修改的场景。
  • XLOCK, HOLDLOCK:获取排他锁并保持到事务结束,其他用户既不能修改也不能读取该记录(默认READ COMMITTED隔离级别下)。适合需要完全锁定、禁止其他用户访问的场景。

2. WPF端的实现步骤

和你之前的短连接模式不同,事务期间需要保持数据库连接打开,直到用户完成操作(保存/取消):

  • 打开连接并启动事务
  • 查询时带上锁定提示,获取目标记录并绑定到WPF UI
  • 等待用户完成编辑操作
  • 根据用户选择提交事务(保存修改)或回滚事务(取消操作)
  • 最后关闭连接

3. 代码示例(C# + WPF)

// 假设selectedOrderId是用户选中的订单ID
int selectedOrderId = 123;

using (var connection = new SqlConnection("你的SQL Server连接字符串"))
{
    connection.Open();
    // 启动事务,默认隔离级别READ COMMITTED足够满足需求
    using (var transaction = connection.BeginTransaction())
    {
        try
        {
            // 查询并锁定订单记录,这里用UPDLOCK,HOLDLOCK允许其他用户读取但不能修改
            var queryCmd = new SqlCommand(
                "SELECT OrderID, CustomerName, OrderStatus, TotalAmount FROM Orders WHERE OrderID = @OrderID WITH (UPDLOCK, HOLDLOCK)",
                connection, transaction);
            queryCmd.Parameters.AddWithValue("@OrderID", selectedOrderId);
            
            using (var reader = queryCmd.ExecuteReader())
            {
                if (!reader.Read())
                {
                    MessageBox.Show("未找到指定订单!");
                    transaction.Rollback();
                    return;
                }
                
                // 将数据映射到实体类,绑定到WPF编辑窗口
                var order = new Order
                {
                    OrderID = (int)reader["OrderID"],
                    CustomerName = reader["CustomerName"].ToString(),
                    OrderStatus = reader["OrderStatus"].ToString(),
                    TotalAmount = (decimal)reader["TotalAmount"]
                };
            }
            
            // 打开WPF编辑窗口,等待用户操作
            var editWindow = new OrderEditWindow(order);
            if (editWindow.ShowDialog() == true)
            {
                // 用户点击保存,执行更新操作
                var updateCmd = new SqlCommand(
                    "UPDATE Orders SET CustomerName = @CustomerName, OrderStatus = @OrderStatus, TotalAmount = @TotalAmount WHERE OrderID = @OrderID",
                    connection, transaction);
                updateCmd.Parameters.AddWithValue("@CustomerName", order.CustomerName);
                updateCmd.Parameters.AddWithValue("@OrderStatus", order.OrderStatus);
                updateCmd.Parameters.AddWithValue("@TotalAmount", order.TotalAmount);
                updateCmd.Parameters.AddWithValue("@OrderID", order.OrderID);
                
                updateCmd.ExecuteNonQuery();
                transaction.Commit();
                MessageBox.Show("订单保存成功!");
            }
            else
            {
                // 用户取消操作,回滚事务释放锁
                transaction.Rollback();
            }
        }
        catch (Exception ex)
        {
            // 发生异常时必须回滚事务,避免长期锁定
            transaction.Rollback();
            MessageBox.Show($"操作失败:{ex.Message}");
        }
        finally
        {
            connection.Close();
        }
    }
}

二、备选方案:乐观锁(适合并发高但冲突少的场景)

如果你的ERP系统并发量很高,不想长时间占用锁资源,可以考虑乐观锁方案:

  • 在Orders表中添加一个RowVersion(时间戳)字段,SQL Server会自动维护这个字段的值
  • 查询时获取该字段的值,编辑完成后更新时,带上这个版本号做判断:
    UPDATE Orders 
    SET CustomerName = @CustomerName, OrderStatus = @OrderStatus
    WHERE OrderID = @OrderID AND RowVersion = @OriginalRowVersion
    
  • 如果更新影响行数为0,说明该记录已经被其他用户修改,此时提示用户重新获取最新数据

这种方式不会锁定行,只是在更新时检查冲突,适合不需要完全阻止读取、只需要避免覆盖修改的场景。

三、重要注意事项

  1. 缩短事务时间:尽量避免让事务长时间处于打开状态(比如用户打开编辑窗口后长时间不操作),可以添加超时机制(比如15分钟无操作自动回滚事务)。
  2. 死锁预防:如果涉及多表或多行锁定,确保所有用户按照相同的顺序锁定资源(比如按OrderID从小到大锁定),避免死锁。
  3. 连接管理:事务期间必须保持连接打开,但要确保在任何异常情况下都能关闭连接,避免连接泄漏。
  4. 性能考量:排他锁(XLOCK)会严重影响并发性能,除非必要,优先使用更新锁(UPDLOCK)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:14:50