C#同ReadCommitted事务下操作SQL Server插入后更新无匹配行问题
问题根因分析
- 自增主键获取逻辑错误:如果
InsertRecordInDB方法中通过@@IDENTITY获取插入后的主键值,当目标表存在插入触发器时,@@IDENTITY会返回触发器操作生成的其他表的自增ID,而非当前插入记录的主键,传入错误的ID自然会导致更新匹配不到行。 - 数据库连接未被事务正确托管:TransactionScope要求所有参与事务的数据库连接必须在TransactionScope的using块内部打开,才会自动纳入当前事务。如果
InsertRecordInDB和UpdateRecordInDB内部的连接是在进入事务块之前打开的,两个操作不会在同一个事务中执行,读提交隔离级别下,插入的记录会被其他并发事务删除/修改,导致后续更新找不到行。 - 事务隐式回滚:TransactionScope默认超时时间为1分钟,如果插入操作执行耗时超过超时阈值,事务会被自动回滚,插入的记录会被撤销,后续更新自然无匹配行。
- 更新语句存在额外过滤条件:如果
UpdateRecordInDB的更新逻辑除了主键匹配外还有其他业务过滤条件(比如WHERE id = @id AND is_valid = 1),刚插入的记录如果被其他并发事务修改了对应字段的值,也会导致更新匹配不到行。
修复方案
- 修正主键获取逻辑:将插入后获取自增ID的逻辑从
@@IDENTITY替换为SCOPE_IDENTITY(),该函数只会返回当前会话、当前作用域生成的最后一个自增ID,不受触发器等外部逻辑影响。 - 调整连接打开时机:确保
InsertRecordInDB、UpdateRecordInDB方法内部的SqlConnection对象的Open()调用,是在TransactionScope的using块执行期间触发的,保证两个操作被纳入同一个事务。 - 调整事务超时配置:如果业务操作耗时较长,可显式设置事务超时时间,避免隐式回滚:
txOpt.Timeout = TimeSpan.FromMinutes(2); // 可根据实际业务调整最长执行时间
- 调整隔离级别(可选):如果业务存在并发修改刚插入记录的场景,可将隔离级别从
ReadCommitted调整为RepeatableRead,避免当前事务插入的记录被其他事务修改或删除,直到当前事务提交/回滚。
内容的提问来源于stack exchange,提问作者Sree
相关产品推荐
相关产品推荐

