如何在执行命令时连接中断场景下使用SqlRetryLogicBaseProvider
问题背景
基于.NET Framework 4.8的WinForms应用中,使用SqlRetryLogicBaseProvider为Azure SQL配置重试逻辑。因客户网络不稳定,执行命令时可能出现连接中断,已将错误码11001加入临时错误列表,但测试时遇到以下问题:
connection.Open()时网络中断,能正常重试;command.ExecuteReader()时网络中断,首次重试后抛出错误:ExecuteReader requires an open and available Connection. The connection's current state is 'closed',且不再重试连接。
疑问:
- 应在何处处理连接状态?
- 是否可在
SqlRetryLogicBaseProvider的Retrying事件中处理? - 处理后是否会触发重试?
原始代码
var options = new SqlRetryLogicOption() { // 重试5次后抛出异常 NumberOfTries = 5, // 重试前的基础延迟间隔 DeltaTime = TimeSpan.FromSeconds(5), // 重试延迟的最大间隔 MaxTimeInterval = TimeSpan.FromSeconds(60), TransientErrors = new List<int> { -1, -2, 0, 109, -2146232060, 596, 1204, 1205, 1222, 49918, 49919, 49920, 4060, 4221, 40143, 40613, 40501, 40540, 40197, 10929, 10928, 10060, 10054, 10053, 997, 233, 64, 18401, 11001 ,1232} }; // 创建指数退避重试逻辑提供器 SqlRetryLogicBaseProvider provider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(options); provider.Retrying += (object s, SqlRetryingEventArgs e) => { LogRetry(e); // 此处是否可以处理连接丢失?如何处理? }; try { using (var connection = new SqlConnection(ConnectionString)) { // 为连接设置重试提供器 connection.RetryLogicProvider = provider; const string selectCmd = "SELECT data FROM mydata WHERE dataID = @dataID"; using (var command = new SqlCommand(selectCmd, connection)) { SqlParameter parameter = new SqlParameter("@dataID", SqlDbType.NVarChar, 9); parameter.Value = did; command.Parameters.Add(parameter); // 为命令设置重试提供器 command.RetryLogicProvider = provider; connection.Open(); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { Console.WriteLine("{0}", reader.GetString(0)); } } } } catch (Exception ex) { return String.Empty; }
编辑1:修改Retrying事件后的问题
修改Retrying事件尝试手动重建连接后,出现内部重试计数器重置为1的问题,导致连接无限重试,无法找到正确更新计数器的方法。
provider.Retrying += (object s, SqlRetryingEventArgs e) => { LogRetry(e); if (s is SqlConnection) { var conn = s as SqlConnection; conn.Close(); conn.Open(); } else { if (s is SqlCommand) { var cmd = s as SqlCommand; cmd.Connection.Close(); cmd.Connection.Open(); } } };
解决方案
问题根源
当ExecuteReader()因网络中断失败时,SQL连接会被自动关闭,但命令的独立重试逻辑不会自动重建连接,导致重试时连接状态非法。而手动在Retrying事件中打开连接的操作,会触发连接自身的重试逻辑,重置内部重试计数器,进而引发无限重试。
正确处理步骤
移除命令的重试提供器设置
无需单独为SqlCommand配置重试提供器,让命令复用SqlConnection的重试逻辑。当命令执行失败触发重试时,连接的重试逻辑会自动处理连接重建,无需手动干预。修改后的核心代码:
try { using (var connection = new SqlConnection(ConnectionString)) { connection.RetryLogicProvider = provider; const string selectCmd = "SELECT data FROM mydata WHERE dataID = @dataID"; using (var command = new SqlCommand(selectCmd, connection)) { SqlParameter parameter = new SqlParameter("@dataID", SqlDbType.NVarChar, 9); parameter.Value = did; command.Parameters.Add(parameter); // 移除此行:command.RetryLogicProvider = provider; connection.Open(); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { Console.WriteLine("{0}", reader.GetString(0)); } } } } catch (Exception ex) { return String.Empty; }限制
Retrying事件的作用范围Retrying事件仅用于记录重试日志、发送通知等辅助操作,不要在其中修改连接或命令的状态,避免干扰重试逻辑的内部计数和流程。验证临时错误码覆盖
确认TransientErrors列表包含所有可能的网络类临时错误码,比如10060(连接超时)、10054(连接被重置)等,确保所有符合条件的错误都能触发重试。
无限重试问题的原因
在Retrying事件中手动调用conn.Open()时,该操作会触发连接自身的重试逻辑,导致重试计数器被重新初始化,每次重试都从第1次开始,永远无法达到NumberOfTries设定的上限,最终引发无限重试。
内容的提问来源于stack exchange,提问作者becky

