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

C# SQL转CSV程序陷入循环且未生成含数据文件问题排查

问题分析与修复方案

核心问题排查

  • 无限循环触发:外层while (reader.HasRows)循环条件错误。DbDataReader.HasRows仅标识初始是否存在数据,读取过程中不会自动变为false,哪怕读完所有数据,该值仍保持初始状态,导致循环无限执行。
  • 文件名记录错误:filesCreated.Add(filePattern)添加的是文件名模板,而非实际生成的curFileName,导致返回的文件列表无效。
  • 多余的流操作:_exportToSplittedCsv中手动调用writer.Close(),但StreamWriter已被using包裹,using块会自动释放资源,手动关闭可能引发流操作异常。
  • SQL硬编码问题:查询语句中的表名固定为table,未使用传入的tableName参数,无法按指定表导出数据。
  • 等待时间不足:Main方法中task.Wait(360)仅等待360毫秒,程序可能未完成执行就被终止,导致文件生成不完整或未生成。

修复后完整代码

namespace csv1
{
    class A
    {
        public static async Task<List<string>> sql_to_csv(string connectionString, string providerName, string tableName, string outputFilepath, Encoding outputFileEncoding, long maxNbOfRows, bool addHeaderRow = true, string separator = ";")
        {
            DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
            using (DbConnection connection = factory.CreateConnection())
            {
                connection.ConnectionString = connectionString;
                await connection.OpenAsync();
                DbCommand command = connection.CreateCommand();
                // 替换硬编码表名,使用传入的tableName参数
                command.CommandText = $"SELECT top 10 * FROM {tableName}";
                long totalRecords = 0;
                long fileCount = 0;
                List<string> filesCreated = new List<string>();
                string filePattern = calculateFilePattern(outputFilepath);
                using (var reader = await command.ExecuteReaderAsync())
                {
                    if (!reader.HasRows)
                        return filesCreated; // 表为空时直接返回空列表
                    
                    bool hasMoreData = true;
                    // 用实际读取行数判断是否还有数据,避免无限循环
                    while (hasMoreData)
                    {
                        string curFileName = string.Format(filePattern, ++fileCount);
                        using (var writer = new System.IO.StreamWriter(curFileName, false, outputFileEncoding))
                        {
                            long rowsWritten = await _exportToSplittedCsv(reader, writer, maxNbOfRows, addHeaderRow, separator);
                            totalRecords += rowsWritten;
                            // 若本次写入行数小于最大限制,说明已读完所有数据
                            hasMoreData = rowsWritten == maxNbOfRows;
                        }

                        filesCreated.Add(curFileName); // 添加实际生成的文件名
                    }
                }
                return filesCreated;
            }
        }

        private static string calculateFilePattern(string path)
        {
            if (path.Contains("{0"))
                return path;

            int extIndex = path.LastIndexOf('.');
            if (extIndex == -1)
                return path + "{0:00000}";
            else
                return path.Substring(0, extIndex) + "{0:00000}" + path.Substring(extIndex);
        }

        private static async Task<long> _exportToSplittedCsv(System.Data.Common.DbDataReader reader, System.IO.StreamWriter writer, long maxNbOfRows, bool addHeaderRow, string separator)
        {
            long rowCount = 0;

            if (addHeaderRow)
            {
                await writer.WriteAsync(getColNames(reader, separator));
                await writer.WriteLineAsync();
            }

            // 读取数据直到读完或达到最大行数限制
            while (await reader.ReadAsync() && (maxNbOfRows < 1 || rowCount < maxNbOfRows))
            {
                await writer.WriteLineAsync(buildCsvRow(reader, separator));
                ++rowCount;
            }
            await writer.FlushAsync();
            // 移除手动Close,由using块自动处理资源释放
            return rowCount;
        }

        private static string getColNames(System.Data.Common.DbDataReader reader, string separator)
        {
            var rowBuilder = new StringBuilder();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                rowBuilder.Append(separator).Append(EscapeCsvValue(reader.GetName(i)));
            }
            return rowBuilder.Remove(0, 1).ToString();
        }

        private static string buildCsvRow(System.Data.Common.DbDataReader reader, string separator)
        {
            var rowBuilder = new StringBuilder();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                object value = reader.GetValue(i);
                string stringValue = reader.IsDBNull(i) ? string.Empty : value.ToString();
                rowBuilder.Append(separator).Append(EscapeCsvValue(stringValue));
            }
            return rowBuilder.Remove(0, 1).ToString();
        }

        // 新增CSV值转义方法,处理特殊字符避免格式错误
        private static string EscapeCsvValue(string value)
        {
            if (value.Contains("\"") || value.Contains(separator) || value.Contains("\n") || value.Contains("\r"))
            {
                return $"\"{value.Replace("\"", "\"\"")}\"";
            }
            return value;
        }

        static void Main(string[] args)
        {
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
            builder.DataSource = "xxxxx";
            builder.InitialCatalog = "xxxxxx";
            builder.IntegratedSecurity = true;
            string provider = "System.Data.SqlClient";

            Task<List<string>> task = sql_to_csv(builder.ConnectionString, provider, "table", "C:\\export_customerOrders{0:00000}.csv", Encoding.Default, 5, true, ",");
            // 等待任务完成,而非固定短时间等待
            task.Wait();
            // 输出生成的文件路径,便于验证结果
            foreach (var file in task.Result)
            {
                Console.WriteLine($"已生成文件:{file}");
            }
        }
    }
}

额外优化说明

  • 添加EscapeCsvValue方法,处理CSV格式中的特殊字符(引号、分隔符、换行等),避免生成的文件格式失效。
  • 在StreamWriter构造中传入指定编码参数,确保文件使用预设编码生成。
  • 通过实际写入行数判断是否还有剩余数据,彻底解决无限循环问题。
  • Main方法中添加文件生成结果输出,方便快速验证执行效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:25:19