如何使用C#使用相同的SQL Server连接运行foreach循环

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用C#使用相同的SQL Server连接运行foreach循环相关的知识,希望对你有一定的参考价值。

我在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 FolderDC_Importsdc_raw.txt";

            try
            {
                StreamReader s = new StreamReader(filename);
                string fileContents = s.ReadToEnd();
                int removeHeader = fileContents.IndexOf('
');
                string contentsNoHeader = fileContents.Substring(removeHeader);
                string contentsFixed = contentsNoHeader.Replace("'", "''");
                string delim = "
";
                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 
" +
                        "Message: " + ex.Errors[i].Message + "
");
                }

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

任何帮助将不胜感激!这可能是我曾尝试编码的最复杂的事情,我必须使用临时表来完成它。

答案

为了展示这看起来如何,我需要稍微移动一下这段代码。如果你将尽可能多的东西分成不同的部分,它会变得容易得多。否则,逻辑和流程变得复杂且难以遵循。这样做的方法不止一种。这只是一个高级示例,展示了如何解决这个问题。

首先,阅读行:

public class FileRowReader
{
    public string[] ReadRows(string filename)
    {
        StreamReader s = new StreamReader(filename);
        string fileContents = s.ReadToEnd();
        int removeHeader = fileContents.IndexOf('
');
        string contentsNoHeader = fileContents.Substring(removeHeader);
        string contentsFixed = contentsNoHeader.Replace("'", "''");
        string delim = "
";
        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部分,以后更容易修改代码以进行更改。 Here's an example.

一次发送它们也意味着您可以在事务中执行一次批量插入或单独插入。这样整个事情成功或整个事情都失败了。您不会处于插入部分数据的奇怪状态。

现在你开始使用的方法不需要做所有这些。它可以只使用这两个类:

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块,或者您可以捕获特定的异常类型,如SqlExceptionIOException。但很可能只是捕捉和记录会告诉你需要知道的一切。

以上是关于如何使用C#使用相同的SQL Server连接运行foreach循环的主要内容,如果未能解决你的问题,请参考以下文章

IIS无法使用SQL Server身份验证连接到SQL Server

ExtJS 使用 C#.net 与 SQL Server 连接

SQL Server ODBC 连接失败

C#:通过 ADO.NET 在 SQL Server 2008 上运行事务

在远程服务器中运行 SQL Server 查询

在 SQL Server 2008 的拆分函数中使用连接表列