首页 > 解决方案 > 向sql插入值时从字符串转换为uniqueidentifier时转换失败

问题描述

我有一个名为 test 的简单 SQL 表,它有两列。第一列是 TINYINT,第二列是 UNIQUEIDENTIFIER 类型。

我创建了使用 for 循环将值插入“测试”表的简单方法,并且它工作正常,没有任何错误。但是一旦我尝试创建字符串到唯一标识符转换错误,它将回滚事务并删除同一事务中所有先前插入的值.

这是发生转换的地方

strCommand += "INSERT INTO Test(Test, Test2) VALUES(" + i.ToString() + ", '" + (i == 251 ? Guid.NewGuid().ToString().Remove(12, 1) : Guid.NewGuid().ToString()) + "'); ";

这是我的完整代码

private static string TryThisPlease()
    {
        SqlConnection connection = null;
        SqlCommand command = null;
        SqlTransaction transaction = null;



        string strRet = "OK";



        try
        {
            connection = new SqlConnection(connectionString);
            connection.Open();



            //starting transaction mode
            transaction = connection.BeginTransaction(IsolationLevel.Snapshot);



            command = new SqlCommand("Test", connection);
            command.CommandType = CommandType.Text;
            command.Transaction = transaction;



            //for (int i = 255; i < 257; i++)
            for (int i = 250; i < 255; i++)
            {
                string[] strData = new string[] { "", "3" };



                string strCommand = "";



                //strCommand += "INSERT INTO Test(Test, Test2) VALUES(" + i.ToString() + ", '" + Guid.NewGuid().ToString() + "'); ";
                strCommand += "INSERT INTO Test(Test, Test2) VALUES(" + i.ToString() + ", '" + (i == 251 ? Guid.NewGuid().ToString().Remove(12, 1) : Guid.NewGuid().ToString()) + "'); ";



                command.CommandText = strCommand;



                if (command.Connection.State != ConnectionState.Open)
                    command.Connection.Open();



                try
                {
                    command.ExecuteNonQuery();
                }
                catch (Exception EX)
                {
                    strRet = "FAIL";



                    try
                    {



                    }
                    catch (Exception)
                    {
                        strRet = "FAIL";
                    }
                }
            }



            transaction.Commit();
        }
        catch (Exception EX)
        {
            transaction.Rollback();



            strRet = "FAIL";
        }
        finally
        {
            connection.Close();
        }



        return strRet;
    }

取消注释已注释的两行并注释掉下面的行,会发生另一个具有相同严重性的错误。在这种情况下事务不会回滚

有什么方法可以防止事务回滚还是我错过了代码中的某些内容?

标签: c#sqlsql-server

解决方案


如果您希望之前的插入成功,您需要做的是在 foreach 循环内创建并提交事务,以便将每一行视为单独的事务。

using(SqlConnection connection = new SqlConnection(connectionString)) {

    connection.Open();

    for (int i = 250; i < 255; i++) {

        using(SqlCommand command = new SqlCommand("", connection, trans)) {
            command.CommandType = System.Data.CommandType.Text;

            using(SqlTransaction trans = connection.BeginTransaction()) {
                try {

                    strCommand = "INSERT INTO Test(Test, Test2) VALUES(" + i.ToString() + ", '" + (i == 251 ? Guid.NewGuid().ToString().Remove(12, 1) : Guid.NewGuid().ToString()) + "'); ";

                    command.CommandText = strCommand;

                    command.ExecuteNonQuery();
                    trans.Commit();
                }
                catch(Exception e) {
                    //Handle Error
                    trans.Rollback();
                }
            }

        }

    }
}

但是,您的命令很容易受到 sql 注入攻击。我建议您将查询参数化,如下所示:

    SqlCommand cmd = new SqlCommand(
        "INSERT INTO Test(Test, Test2) VALUES(@id1,@id2)", conn);

cmd.Parameters.Add( new SqlParameter(@id1, SqlDbType.Int)).Value = i;
cmd.Parameters.Add( new SqlParameter(@id2, SqlDbType.Guid)).Value = (i == 251 ? Guid.NewGuid().ToString().Remove(12, 1) : Guid.NewGuid().ToString());

更新 如果您仍想使用批量事务,您可以考虑事务的保存点。您可以回滚到保存点,而不是回滚整个事务。阅读有关 Savepoint 的更多信息

                        command.CommandText = strCommand;
                        trans.Save($"save{i}");
                        command.ExecuteNonQuery();
                        trans.Commit();
                    }
                    catch(Exception e) {
                        //Handle Error
                        trans.Rollback($"save{i}");
                        trans.Commit();
                    }

推荐阅读