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

.NET 3.5下SQL查询偶发远程主机强制关闭现有连接异常

问题根因

这个偶发报错是.NET Framework 3.5自带的System.Data.SqlClient的已知行为差异,.NET 4.0及以上版本修复了连接池的空闲连接校验逻辑,所以不会复现:

  • .NET 3.5下的SqlClient连接池默认不会校验取出的空闲连接是否存活,SQL Server服务端会按配置自动关闭闲置超过阈值(默认30分钟,部分实例会配置得更短)的TCP连接。你每次调用方法新建SqlConnection时,实际是从连接池复用已有TCP连接,一旦取到已经被服务端单方面关闭的“死连接”,执行ExecuteReader()时就会抛出这个远端强制关闭连接的传输层错误。
  • 因为连接池里的死连接是随机被取到的,所以报错呈现偶发特征,和你描述的“两次操作成一次”的概率完全匹配。
修复方案

1. 调整连接字符串配置(改造成本最低)

在你现有连接字符串末尾追加以下参数,让SqlClient从连接池取连接时自动做存活校验:

;Connection Lifetime=3;Pooling=true;Connection Reset=true;Validate Connection=true

Validate Connection=true会在连接取出时发一个极小的探测包确认连接可用,发现是死连接就自动丢弃创建新连接,性能损耗可以忽略,能解决90%以上的偶发断连问题。

2. 增加针对性的错误重试逻辑

连接校验无法覆盖网络瞬断场景,针对你这个获取表名的轻量操作,加1-2次定向重试即可完全消除偶发报错,注意不要捕获到SqlException就直接返回空结果,只对连接类错误重试,其他权限、语法类错误直接记录返回即可。
核心修改后的代码参考:

public List<string> GetAllTables(string sDatabase)
{
    List<string> result = new List<string>();
    const int maxRetry = 2;
    int retryTimes = 0;

    while (retryTimes <= maxRetry)
    {
        try
        {
            using (SqlConnection con = new SqlConnection(dbConnection.conString + sDatabase))
            {
                con.Open();
                using (SqlCommand command = new SqlCommand("SELECT name FROM sys.Tables", con))
                {
                    command.CommandTimeout = 10;
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            result.Add(reader["name"].ToString());
                        }
                    }
                }
            }
            return result;
        }
        catch (SqlException sqlEx)
        {
            // 仅对传输层断连错误做重试,错误号0/10053/10054均对应连接被强制关闭类问题
            bool isConnectionError = (sqlEx.Number == 0 || sqlEx.Number == 10053 || sqlEx.Number == 10054);
            retryTimes++;
            if (!isConnectionError || retryTimes > maxRetry)
            {
                string methodName = new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name;
                string className = new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().DeclaringType.Name;
                Debug.Print("Error in class {0} - method: {1}: {2}", className, methodName, sqlEx.Message);
                return result;
            }
            result.Clear();
            System.Threading.Thread.Sleep(200);
        }
    }
    return result;
}

3. 优化调用逻辑减少无效连接

你当前循环5-10次调用GetAllTables属于完全不必要的开销,完全可以在单次恢复操作启动时调用一次该方法,把拿到的表名列表缓存下来,后续流程直接复用缓存结果即可,既减少连接调用次数降低碰死连接的概率,也能提升程序运行速度。

避坑提示
  • 不要为了绕开这个问题直接把连接字符串的Pooling设为false关闭连接池,那样每次调用都会新建TCP连接,性能会下降一个量级,多表恢复时速度会非常慢。
  • 检查目标SQL Server实例的连接空闲超时配置,不要设置低于5分钟,否则高频率调用时即使开了连接校验也容易碰到死连接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:18:28