C# 脚本在 运行 下一步之前未完成 SQL 查询

C# script not completing a SQL Query before running the next step

我有一个 c# 脚本 运行 作为更大代码的一部分。它应该执行 sql 脚本,然后通过 SFTP 连接发送生成的 .csv 文件。

问题是查询似乎在查询执行完成之前发送数据。有没有办法确保在我的程序执行下一步之前完成查询?

下面 SQL 部分的代码。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace SQLtoCSV
{
    static class SQL
    {
        public static DataTable GetData(string strQuery)
        {
            var cmd = new SqlCommand(strQuery);
            var dt = new DataTable();
            var strConnString = ConfigurationManager.ConnectionStrings["RSConString"].ConnectionString;
            Logger.WriteLog("Used RS connection string: {0}", strConnString);
            var con = new SqlConnection(strConnString);
            var sda = new SqlDataAdapter();
            cmd.CommandType = CommandType.Text;
            cmd.CommandTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["RSTimeout"]);
            cmd.Connection = con;
            try
            {
                con.Open();
                sda.SelectCommand = cmd;
                sda.Fill(dt);
                return dt;
            }
            catch (Exception ex)
            {
                Logger.WriteLog("SQL GetData trown exception, see next entry.");
                Logger.WriteLog(ex);
                return null;
            }
            finally
            {
                con.Close();
                sda.Dispose();
                con.Dispose();
            }
        }
    }
}

这里是主要的控制流程。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.IO;

namespace SQLtoCSV
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string sourceDirectory = ConfigurationManager.AppSettings["LocalSQLdirectory"].ToString();
                List<string> txtFiles = Directory.EnumerateFiles(sourceDirectory, "*.sql", SearchOption.AllDirectories).ToList();

                Logger.WriteLog("Found {0} SQL files in local dir '{1}'.", txtFiles.Count, sourceDirectory);

                foreach (string currentFile in txtFiles)
                {
                    string strSql = File.ReadAllText(currentFile);
                    Logger.WriteLog("Executing '{0}'...", currentFile);
                    var dtTable = SQL.GetData(strSql);
                    Logger.WriteLog("Done.");
                    var tmpFile = currentFile + ".tmp";
                    TableToCSV.ConvertDtTableToCSV(dtTable, tmpFile);
                    var name = Path.GetFileNameWithoutExtension(currentFile);
                    SFTP.upload(tmpFile, name);
                    File.Delete(tmpFile);
                }
            }
            catch (Exception ex)
            {
                Exception exMail = null;
                Logger.WriteLog("The main app trown exception, see next entry");
                try
                {
                    var msg = "CSV to SQL application thrown exception: \r\n" +
                              "\tSource: " + ex.Source + "\r\n" +
                              "\tMessage: " + ex.Message + "\r\n" +
                              "Stack: \r\n" + ex.StackTrace + "\r\n";
                    if (ex.InnerException != null)
                    {
                        msg += "Inner exception: \r\n" +
                               "\tSource: " + ex.InnerException.Source + "\r\n" +
                               "\tMessage: " + ex.InnerException.Message + "\r\n" +
                               "\tStack: " + ex.InnerException.StackTrace + "\r\n";
                    }
                    MailHelper.Send_Mail(msg, "SQL to CSV error");

                }
                catch(Exception ex2) {
                    exMail = ex2;
                }
                Logger.WriteLog(ex);
                if (exMail != null)
                {
                    Logger.WriteLog("Cannot send a mail, see next entry");
                    Logger.WriteLog(exMail);
                }
            }
        }
    }
}

TableToCSV 部分

using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Text;
using System.IO;

namespace SQLtoCSV
{
    static class TableToCSV
    {
        public static void ConvertDtTableToCSV(DataTable dt, string filePath)
        {
            string tempPath = System.IO.Path.GetTempPath();

            using (var sw = new StreamWriter(filePath, false, Encoding.UTF8))
            {
                var columnNames = dt.Columns.Cast<DataColumn>().Select(column => column.ColumnName);
                sw.WriteLine(string.Join(",", columnNames));

                foreach (DataRow row in dt.Rows)
                {
                    IEnumerable<string> fields = row.ItemArray.Select(field => /*string.Concat("\"", */field.ToString()/*.Replace("\"", "\"\""), "\"")*/);
                    sw.WriteLine(string.Join(",", fields));
                }
            }
        }
    }
}

SFTP 部分

using System;
using System.Configuration;
using Renci.SshNet;
using System.IO;

namespace SQLtoCSV
{
    static class SFTP
    {
        public static void upload(string tmpFile, string fileName)
        {
            try
            {
                var host = ConfigurationManager.AppSettings["SFTPhost"].ToString();
                var port = Convert.ToInt32(ConfigurationManager.AppSettings["SFTPport"]);
                var username = ConfigurationManager.AppSettings["SFTPuser"].ToString();
                var password = ConfigurationManager.AppSettings["SFTPpassword"].ToString();
                var workingdirectory = ConfigurationManager.AppSettings["SFTPdirectory"].ToString();
                var fileExtension = ConfigurationManager.AppSettings["UploadedFileExtension"].ToString();
                var timeout = Convert.ToInt32(ConfigurationManager.AppSettings["SFTPtimeout"]);
                var timestamp = ConfigurationManager.AppSettings["SFTPtimestamp"].ToString();

                fileName += DateTime.Now.ToString(timestamp) + ".csv";

                using (var client = new SftpClient(host, port, username, password))
                {
                    client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(timeout);
                    client.Connect();
                    Logger.WriteLog("Connected to {0}", host);

                    client.ChangeDirectory(workingdirectory);
                    Logger.WriteLog("Changed directory to {0}", workingdirectory);

                    using (var fileStream = new FileStream(tmpFile, FileMode.Open))
                    {
                        Logger.WriteLog("Uploading {0} ({1:N0} bytes)", fileName, fileStream.Length);

                        // bypass Payload error large files
                        client.BufferSize = 4 * 1024;
                        client.UploadFile(fileStream, fileName);
                    }
                }
                Logger.WriteLog("The file '{0}' is uploaded");
            }
            catch(Exception ex)
            {
                Logger.WriteLog("The SFTP.upload function trown exception, see next entry");
                Logger.WriteLog(ex);
                throw;
            }
        }

        private static Stream StreamFromString(string s)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }
    }
}

好的,CSV 部分是你的问题。 StreamWriter 默认情况下会进行缓冲,因此您的最后一块数据很可能不会写入磁盘。在结束 using 语句之前添加 sw.Flush();,如下所示:

    public static void ConvertDtTableToCSV(DataTable dt, string filePath)
    {
        string tempPath = System.IO.Path.GetTempPath();

        using (var sw = new StreamWriter(filePath, false, Encoding.UTF8))
        {
            var columnNames = dt.Columns.Cast<DataColumn>().Select(column => column.ColumnName);
            sw.WriteLine(string.Join(",", columnNames));

            foreach (DataRow row in dt.Rows)
            {
                IEnumerable<string> fields = row.ItemArray.Select(field => /*string.Concat("\"", */field.ToString()/*.Replace("\"", "\"\""), "\"")*/);
                sw.WriteLine(string.Join(",", fields));
            }
            sw.Flush();
        }
    }

不想这么说,但是代码本身是正确的,尽管缺少刷新部分。那就是说问题出在配置文件上,导致数据从测试环境而不是生产环境中提取。

由于两者的结果集通常是镜像的,因此发现了细微的差别。我想这个故事的寓意是,如果您看到与您所看到的理论图不符的非常奇怪的结果,那么退后一步并确保基础是正确的是值得的。

感谢所有提供帮助的人!