【问题标题】:Why is my SqlConnection entering a broken state during execution?为什么我的 SqlConnection 在执行过程中会进入中断状态?
【发布时间】:2015-11-14 22:53:42
【问题描述】:

我有一个简单的控制台应用程序,它正在执行一系列存储过程大约 150,000 次。其目的是将初始数据库数据导入数据库的新实例。

它在工作中运行良好,但在家里我尝试使用 Sql Server Express,一切看起来都很好,但在完成 25.76% 时,sql 连接进入断开状态并生成一个 InvalidOperation 异常,说明连接断开并且不能用于执行下一条命令。

我明白这意味着什么,但我不明白为什么它会损坏,以及为什么每次尝试运行控制台应用程序都会在完全相同的时间发生这种情况。

它每次都在 25.76% 完成时进入损坏状态,总是在那个位置,并且总是在正在执行的 SQL 的同一行上。

异常发生时正在执行的行如下:

EXEC [geo].[addUpdateRegion] @countryCode = N'CG', @regionCode = N'08', @regionName = N'Plateaux', @initData = 1

它会导致 InvalidOperationException:BeginExecuteNonQuery 需要打开且可用的连接。连接的当前状态:断开。”

我很困惑,因为我完全禁用了超时。所以我想知道 SQLExpress 是否对您可以在同一连接上执行的命令数有限制?

这就是代码的样子:

using log4net;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace XYZ.SetupData
{
    class Program
    {
        static ILog logger = null;
        static string connectionString = null;
        static SqlConnection conn = null;
        static double lineCount = 1;
        static double lineIndex = 1;
        static Program()
        {
            logger = LogManager.GetLogger(typeof(Program));
            log4net.Config.XmlConfigurator.Configure();
            var connSettings = ConfigurationManager.ConnectionStrings["theDb"];
            if (connSettings == null || string.IsNullOrEmpty(connSettings.ConnectionString))
                throw new ConfigurationErrorsException("connectionString theDbwas not found or is empty.");
            connectionString = connSettings.ConnectionString;
            conn = new SqlConnection(connectionString);
            conn.Open();
        }

        static void Main(string[] args)
        {
            var baseDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "sqlFiles");
            var files = baseDir.GetFiles("*.sql", SearchOption.TopDirectoryOnly).Select(file => file.FullName).ToArray();
            lineCount = (double)GetTotalFileLines(files);

            foreach (var file in files)
            {
                ExecuteFileLines(file);
            }
            Console.ReadKey(true);
        }

        #region Utility
        static void WriteProgress(string command)
        {
            double percent = (lineIndex / lineCount) * 100;
            Console.Write("\r Percent Complete: {0}%   ", Math.Round(percent, 2));
            ++lineIndex;
        }

        static int GetTotalFileLines(params string[] fileNames)
        {
            int total = 0;
            foreach (var fileName in fileNames)
                total += (File.ReadLines(fileName).Where(line => !string.IsNullOrEmpty(line) && !line.StartsWith("--")).Count());
            return total;
        }
        static void ExecuteFileLines(string fileName)
        {
            TryRun(() =>
            {
                if (string.IsNullOrEmpty(fileName))
                    throw new ArgumentNullException("fileName");
                if (!File.Exists(fileName))
                    throw new FileNotFoundException("file: " + fileName + " was not found!");
                IEnumerable<string> fileLines = File.ReadLines(fileName).Where(line => !string.IsNullOrEmpty(line) && !line.StartsWith("--"));


                LogInfo("--Staring Execution: " + fileName);
                foreach (var line in fileLines)
                {
                    RunSqlConnection(conn =>
                    {
                        LogInfo("RUNNING | " + line);
                        WriteProgress(line);
                        try
                        {
                            SqlCommand cmd = new SqlCommand(line, conn);
                            cmd.BeginExecuteNonQuery();
                        }
                        catch (Exception ex)
                        {
                            int i = 0;
                        }
                    });
                }
            });
        }
        static void RunSqlConnection(Action<SqlConnection> callBack)
        {
            try
            {
                callBack(conn);
            }
            catch (Exception ex)
            {
                LogError(ex);
                throw ex;
            }
        }
        static void TryRun(Action callBack)
        {
            try
            {
                callBack();
            }
            catch (Exception ex)
            {
                LogError(ex);
                throw ex;
            }
        }
        static void LogError(Exception ex)
        {
            logger.Error(ex.Message, ex);
            Console.WriteLine(ex.ToString());
        }

        static void LogInfo(string message, params object[] parameters)
        {
            logger.Info(string.Format(message, parameters));
        }

        static void LogDebug(string message, params object[] parameters)
        {
            logger.Debug(string.Format(message, parameters));
        }
        #endregion
    }
}

【问题讨论】:

  • 你应该检查Comparison of Sql Server Editions。您可能已达到每个实例 1 GB 的内存限制。
  • @OguzOzgul 我完全忘记了这一点,这可能正是它的本质。会解释为什么它每次都同时发生。
  • 如果我输入这个作为答案,你会接受吗?
  • 当然如果是这样的话。如果没有,我想知道它是什么,所以也请告诉我们。
  • 就是这样,是的,我会的。我添加了一个重置​​计数器,每执行 500 次就重新建立连接,问题就解决了。

标签: c# stored-procedures console-application sqlconnection sql-server-2014-express


【解决方案1】:

您正试图在多个线程上处理工作。有一个错误是您同时使用相同的连接。这是不允许的。由于这是一个竞争条件,任何事情都可能在内部发生。

我不确定您为什么要使用 APM 模式,因为它已经过时了。我认为 PLINQ 加上同步 IO 加上每个工作项一个连接实际上非常适合您的场景。

【讨论】:

  • 我不确定我在多个线程上的情况。它是一个控制台应用程序,全部位于 Main 方法之外的 Program 类中。如果我理解正确,它应该是同步的。
  • 您正在使用异步 IO (BeginExecuteNonQuery),因此它是非同步的。您正在为同一 conn 上的每一行启动一个异步 IO。这使它具有并发性和活力。
  • 另一个错误是永远不会调用 EndExecuteQuery。这违反了 APM 协议并泄漏资源。
  • 我在哪里使用异步......我正在使用 lambda 表达式来同步调用方法,内联。除非我使用的某些 lambda 默认是异步的,否则我不知道。
  • AHHH 我的意思是使用 ExecuteNonQuery(),谢谢你的理解。那是我看到的异步调用。伙计,我现在感觉自己像个小疙瘩,哈哈。不敢相信我错过了。智能感知错误...
【解决方案2】:

您应该检查 Sql Server 版本的比较。

您可能会达到 Sql Express Edition 的每个实例 1 GB 的内存限制。

【讨论】:

  • 这不是完整的解释,因为如果达到内存限制,应该会出现错误。我认为OP在接受它时犯了一个错误。他的重置策略有效并不意味着实例内存限制是罪魁祸首。他的整个解决方案都被打破了。我也不容忍纯粹的猜测答案。
  • 这在你达到 1GB 的限制时很有用,但实际上是因为我错误地调用了异步执行方法。我将其更改为 ExecuteNonQuery 并且运行良好。
  • 谢谢,解决您的问题很重要。但是,您如何在工作中成功执行所有这 15 万次操作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 2011-09-26
  • 2019-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多