Как запустить цикл foreach, используя то же соединение с SQL Server, используя C # - PullRequest
0 голосов
/ 22 апреля 2019

У меня есть строковый массив в программе на C #, который мне нужно загрузить во временную таблицу в SQL Server. В настоящее время я использую цикл foreach для запуска запроса на вставку. Когда соединение SQL закрывается, временная таблица исчезает, поэтому я получаю таблицу, в которой есть только 1 строка, когда в массиве сотни.

Я попытался добавить

using (SqlConnection sqlconnection2 = new SqlConnection()) 

оператор в верхней части метода (удаление существующей линии соединения, внутри оператора If(ConnSucceeds), только внутри блока try и внутри цикла foreach. Когда он находится внутри цикла foreach, у меня возникает та же проблема. где-нибудь еще я получаю сообщение об ошибке, в котором говорится, что соединение не открыто.

В конечном итоге мне нужно будет добавить хотя бы еще один цикл foreach для запуска другого SQL-запроса для манипулирования данными, а затем найти способ экспортировать его в текстовый файл, используя одно и то же соединение.

    private void ImportToTempTable()
    {
        this.GetProgramInfoForSQLQuery();
        this.GetInstallFolder();
        string config = this._InstallFolder + @"\" + this._Version + @"\" + this._brandName + @".exe.config";
        GetInstanceName(config);

        string connStr = "<proprietary conn string parameters>";
        bool ConnSucceeds = false;

        SqlConnection sqlConnection = new SqlConnection();
        StringBuilder errorMessages = new StringBuilder();

        if (!ConnSucceeds)
        {
            try
            {
                sqlConnection.ConnectionString = connStr;
                sqlConnection.Open();
                this.WriteNote("SQL Connection Succeeded");
                this.WriteNote("");
                ConnSucceeds = true;
            }
            catch (Exception ex)
            {
                ProjectData.SetProjectError(ex);
                int num = (int)Interaction.MsgBox((object)(@"Unable to connect to SQL Server:" + sqlConnection.ConnectionString + @"
                Does the " + this._brandName + " Database Live on this Machine?"), MsgBoxStyle.Exclamation, (object)"SQL Connection Error");
                ProjectData.ClearProjectError();
            }
        }

        if (ConnSucceeds)
        {
            string filename = @"C:\Program Folder\DC_Imports\dc_raw.txt";

            try
            {
                StreamReader s = new StreamReader(filename);
                string fileContents = s.ReadToEnd();
                int removeHeader = fileContents.IndexOf('\n');
                string contentsNoHeader = fileContents.Substring(removeHeader);
                string contentsFixed = contentsNoHeader.Replace("'", "''");
                string delim = "\n";
                string[] Rows = contentsFixed.Split(delim.ToCharArray());

                foreach (string row in Rows)
                {
                    string query = @"USE DBName IF (NOT EXISTS (SELECT * FROM tempdb.sys.tables WHERE name LIKE '%#DCImportTable%'))
BEGIN
CREATE TABLE #DCImportTable (Main varchar (8000));
INSERT INTO #DCImportTable (Main) VALUES ('" + row + @"');
END
ELSE 
INSERT INTO #DCImportTable (Main) VALUES ('" + row + "');";

                        SqlCommand command = new SqlCommand(query, sqlConnection);
                        command.ExecuteNonQuery();
                        this.WriteNote(row);                  
                }

                this.WriteNote("Check Table");
                this.WriteNote("");
            }
            catch (SqlException ex)
            {
                for (int i = 0; i < ex.Errors.Count; i++)
                {
                    errorMessages.Append("Error \n" +
                        "Message: " + ex.Errors[i].Message + "\n");
                }

                this.WriteNote(errorMessages.ToString());
                sqlConnection.Close();
                this.WriteNote("SQL Connection Terminated");
            }
        }
        else
        {
            this.WriteNote("SQL Login Incorrect");
            sqlConnection.Close();
            this.WriteNote("SQL Connection Terminated");
        }
}

Любая помощь будет принята с благодарностью! Это, наверное, самая сложная вещь, которую я когда-либо пытался кодировать, и для этого мне нужно использовать временные таблицы.

1 Ответ

0 голосов
/ 22 апреля 2019

Чтобы показать, как это может выглядеть, мне нужно немного передвигаться по частям этого кода. Будет намного легче, если вы разделите как можно больше на разные части. В противном случае логика и поток становятся сложными и трудными для понимания. Есть несколько способов сделать это. Это просто высокоуровневый пример, показывающий, как вы можете разбить это.

Сначала прочитайте строки:

public class FileRowReader
{
    public string[] ReadRows(string filename)
    {
        StreamReader s = new StreamReader(filename);
        string fileContents = s.ReadToEnd();
        int removeHeader = fileContents.IndexOf('\n');
        string contentsNoHeader = fileContents.Substring(removeHeader);
        string contentsFixed = contentsNoHeader.Replace("'", "''");
        string delim = "\n";
        return contentsFixed.Split(delim.ToCharArray());
    }
}

Сама по себе эта часть гораздо легче понять. (Я потратил всего несколько секунд на имена. Было бы хорошо переименовать их во что-то более наглядное.)

Затем запишите строки в SQL. Опять же, для простоты, это может быть отдельным от всей инициализации и конфигурации. Я не рассматриваю вопрос о том, имеет ли смысл выполнять одну команду для каждой строки, или риск внедрения SQL-кода, который возникает, когда мы строим SQL путем объединения строк. SQL следует изменить так, чтобы он использовал параметры.

public class SqlRowWriter
{
    private readonly string _connectionString;

    public SqlRowWriter(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void WriteRows(string[] rows)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            foreach (var row in rows)
            {
                // Write each row
            }
        }
    }
}

Одним из преимуществ этого подхода является то, что несвязанные части кода отделены. Часть, которая читает из текстового файла, не знает, что вы собираетесь делать с текстом. Это не волнует. Его работа - просто читать и давать вам текст.

Часть, которая пишет в SQL, не заботится о том, откуда поступили данные. Он просто берет все данные, которые вы ему предоставляете, и оперирует ими. Каждая часть проще и легче для понимания, потому что она делает одно, а не пытается совмещать сразу несколько.

Еще одним преимуществом является то, что оба класса теперь легче тестировать. Если вы хотите убедиться, что ваш читатель работает, проверьте это. Если вы хотите проверить, работает ли ваш писатель, проверьте это. Вам не нужно проверять их обоих одновременно.

Я не включил часть, которая собирает и записывает отдельные ошибки SQL. Вы можете добавить его обратно. Причина, по которой я его пропустил, состоит в том, что было бы очень трудно понять, что делать дальше, если вы попытаетесь написать 50 строк, но 25 из них выдают исключения.

Что я особенно рекомендую, так это пытаться отправлять все строки в SQL одновременно, а не отправлять их по одной строке за раз. Это можно сделать, используя хранимую процедуру на стороне SQL Server (вместо передачи всей команды SQL в виде строки), а затем передав все строки в виде табличного параметра. Это похоже на отправку строк данных в одном исполнении вместо нескольких. Это немного другой ответ, но если оставить SQL-часть этого отдельного элемента, вам будет гораздо легче изменить ваш код позже, чтобы внести это изменение. Вот пример.

Отправка их всех одновременно означает также, что вы можете выполнить одну массовую вставку или отдельные вставки в транзакции. Таким образом, все удастся или все рухнет. Вы не останетесь в странном состоянии, в которое была вставлена ​​часть ваших данных.

Теперь метод, с которого вы начали, не должен делать все это. Он может просто использовать эти два других класса:

private void ImportToTempTable()
{
    // I don't know what this does. I just left it in because it's 
    // part of your original code.
    this.GetProgramInfoForSQLQuery();
    this.GetInstallFolder();
    string config = this._InstallFolder + @"\" + this._Version + @"\" + this._brandName + @".exe.config";
    GetInstanceName(config);

    string filename = "<whatever your file name is>";
    string connStr = "<proprietary conn string parameters>";
    var rowReader = new FileRowReader();
    var rows = rowReader.ReadRows(filename);
    var writer = new SqlRowWriter(connStr);
    writer.WriteRows(rows);
}

Что если вы прочитаете все строки, но затем захотите проверить их все, чтобы заранее выяснить, есть ли в них что-то, что вы не хотите писать в SQL, например пустые строки или заголовки? Теперь действительно легко вставить другой класс или метод, который берет массив строк и фильтрует их или как-то их очищает.

Я еще не включил обработку исключений. Это также проще, если вы делаете это во внешнем методе. Если вы хотите обрабатывать все исключения из чтения или записи одинаково, вы можете просто сделать это:

private void ImportToTempTable()
{
    // I don't know what this does. I just left it in because it's 
    // part of your original code.
    this.GetProgramInfoForSQLQuery();
    this.GetInstallFolder();
    string config = this._InstallFolder + @"\" + this._Version + @"\" + this._brandName + @".exe.config";
    GetInstanceName(config);

    string filename = "<whatever your file name is>";
    string connStr = "<proprietary conn string parameters>";
    try
    {
        var rowReader = new FileRowReader();
        var rows = rowReader.ReadRows(filename);
        var writer = new SqlRowWriter(connStr);
        writer.WriteRows(rows);
    }
    catch (Exception ex)
    {
        // log the exception
    }
}

Даже если вы не добавите больше подробностей, будет легко определить, является ли это исключением при чтении файла или при записи в SQL.Если это SQL, будет легко определить, произошло ли это из-за открытия соединения или выполнения команды.Скорее всего, это скажет вам все, что вам нужно.Если вам нужно больше подробностей, вы можете поместить отдельные блоки try / catch вокруг различных вызовов методов, или вы можете перехватить определенные типы исключений, такие как SqlException или IOException.Но есть вероятность, что только перехват и регистрация покажут вам все, что вам нужно знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...