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

生产环境SQL Server Upsert改造:添加Rowversion并发检查并抛出可捕获错误

这个需求很典型,我帮你拆解下具体实现步骤,既能用Rowversion做乐观锁校验,还能抛出C#能精准捕获的专属错误,给用户清晰的并发冲突提示:

核心思路:Rowversion乐观锁 + 自定义SQL错误抛出

通过给业务表添加Rowversion字段自动维护行版本,在Upsert存储过程中加入版本校验逻辑,当检测到并发冲突时抛出带特定标识的自定义错误,让C#端可以精准识别并转化为用户友好提示。


1. 给业务表添加Rowversion字段

首先确保你的业务表有一个Rowversion类型的字段(SQL Server会自动维护,每次行变更都会更新该值)。如果还没有,执行以下SQL添加:

ALTER TABLE YourBusinessTable ADD RowVersionStamp ROWVERSION;

2. 改造Upsert存储过程,加入并发校验+自定义错误抛出

修改原有存储过程,加入Rowversion校验逻辑,当校验失败(行已被其他用户修改)时,抛出带专属错误号的自定义错误(错误号需在50000以上,属于SQL Server预留的自定义错误区间):

CREATE PROCEDURE dbo.UpsertYourEntity
    @Id INT,
    @NewValue NVARCHAR(100),
    @CurrentRowVersion BINARY(8) -- 从C#端传入的当前行版本
AS
BEGIN
    SET NOCOUNT ON;

    -- 带版本校验的更新操作
    UPDATE YourBusinessTable
    SET YourColumn = @NewValue
    WHERE Id = @Id AND RowVersionStamp = @CurrentRowVersion;

    -- 判断更新是否生效
    IF @@ROWCOUNT = 0
    BEGIN
        -- 先区分“行不存在”和“并发冲突”两种情况
        IF EXISTS(SELECT 1 FROM YourBusinessTable WHERE Id = @Id)
        BEGIN
            -- 抛出并发冲突专属错误,错误号用50001(可自定义)
            THROW 50001, '并发冲突:该数据已被其他用户修改,请刷新后重试。', 1;
        END
        ELSE
        BEGIN
            -- 行不存在则执行插入逻辑
            INSERT INTO YourBusinessTable(Id, YourColumn)
            VALUES(@Id, @NewValue);
        END
    END
END

注:SQL Server 2016及以上版本推荐用THROW替代RAISERROR,它默认错误级别为16(用户可捕获),还会保留错误堆栈便于调试。

3. C#端精准捕获并发冲突错误

在调用存储过程的C#代码中,捕获SqlException并检查错误号,识别出并发冲突后给用户友好提示:

// 可定义常量统一错误号,避免硬编码不一致
private const int ConcurrencyConflictErrorNumber = 50001;

try
{
    using (var conn = new SqlConnection("YourConnectionString"))
    {
        conn.Open();
        using (var cmd = new SqlCommand("dbo.UpsertYourEntity", conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@Id", targetEntityId);
            cmd.Parameters.AddWithValue("@NewValue", updatedValue);
            // 传入之前查询获取的当前行版本
            cmd.Parameters.AddWithValue("@CurrentRowVersion", currentRowVersion);
            
            cmd.ExecuteNonQuery();
        }
    }
}
catch (SqlException ex)
{
    // 检查是否是并发冲突专属错误
    if (ex.Errors.Cast<SqlError>().Any(e => e.Number == ConcurrencyConflictErrorNumber))
    {
        // 给用户显示友好提示
        MessageBox.Show(ex.Message, "操作提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        // 可选:自动刷新数据,让用户看到最新版本
        RefreshCurrentData();
    }
    else
    {
        // 其他SQL错误按原有逻辑处理
        throw;
    }
}

额外优化建议

  • 插入新行后,可将自动生成的Rowversion值返回给C#端,方便后续编辑时携带版本信息;
  • 可以把错误消息也封装成常量,SQL和C#端统一使用,避免文案不一致;
  • 如果是分布式系统,可结合事务确保校验和更新的原子性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:04:45