SqlServerRetryingExecutionStrategy不支持用户发起事务的原因及SQL Server 2014适配方案
问题解答
报错是否和SQL Server版本相关
这个报错本质是EF Core的SqlServerRetryingExecutionStrategy的内置限制,SQL Server版本差异是触发条件而非根本原因:
- EF Core的重试执行策略默认会校验用户手动开启的事务,避免重试时出现事务状态不一致的问题
- SQL Server 2019及更高版本支持更高版本的TDS协议和透明事务重试逻辑,该校验逻辑在高版本SQL Server下不会被触发,所以你本地运行正常;SQL Server 2014不支持对应特性,就会触发校验抛出报错
修复方案
- 首先修正代码核心bug:你在using块中实例化了
posDbCtx上下文、开启了事务,但所有数据库操作(Add、SaveChanges)都使用了外部的_posDbContext实例,两个上下文完全独立,事务根本没生效,这也是触发报错的核心原因之一 - 把两个相关的数据库操作合并到同一个执行策略执行块中,所有操作都使用当前块内的上下文实例,避免跨上下文操作事务
正确代码示例:
using(var posDbCtx = new PosDbContext()) { var strategy = posDbCtx.Database.CreateExecutionStrategy(); // 所有事务逻辑都放在Execute的回调内 strategy.Execute(() => { using (var transaction = posDbCtx.Database.BeginTransaction()) { // 所有操作都用当前块内的posDbCtx,不要混用外部上下文 posDbCtx.PrintJobs.Add(_printJobs); posDbCtx.SaveChanges(); int lineindex = 0; foreach (var jline in _printjoblines) { var pjobline = new PrintJobLine { PrintLineId = Guid.NewGuid(), PrintJobId = jline.PrintJobId, LineIndex = lineindex, Text = jline.Text, RightText = jline.RightText, LeftText = jline.LeftText, LineLength = jline.LineLength, PrintOptions = jline.PrintOptions, ItemName = jline.ItemName, Quantity = jline.Quantity, Flags = jline.Flags, BergPlu = jline.BergPlu, ActualPrice = jline.ActualPrice, BasePrice = jline.BasePrice, Vducolor = jline.Vducolor, ParentIndex = jline.ParentIndex, Peplu = jline.Peplu, SaleItemId = jline.SaleItemId, SaleTenderId = jline.SaleTenderId, SignatureId = jline.SignatureId }; posDbCtx.PrintJobLines.Add(pjobline); lineindex++; } // 批量提交即可,不需要循环内每次调用SaveChanges,可大幅提升性能 posDbCtx.SaveChanges(); transaction.Commit(); } }); }
补充说明
- 如果你两个操作确实不需要在同一个事务内,可以分开写,但每个执行策略块内部都必须使用当前块内实例化的上下文操作,不能混用外部上下文
- 如果你的业务场景不需要重试策略,可以在DbContext的配置中移除
EnableRetryOnFailure配置,也可以规避这个报错,但更推荐按照上述正确写法使用重试策略,提升弱网环境下的操作成功率
内容的提问来源于stack exchange,提问作者Mik AA
相关产品推荐
相关产品推荐

