在 C# 中构建具有多个参数的高效 SQL 语句

Build efficient SQL statements with multiple parameters in C#

我有一个包含不同 ID 的项目列表,这些 ID 代表 SQL table 的 PK 值。 有什么方法可以建立一个高效安全的声明吗?

从现在开始,我一直准备一个表示语句的字符串,并在通过 foreach 循环遍历列表时构建它。

这是我正在做的一个例子:

string update = "UPDATE table SET column = 0 WHERE";

foreach (Line l in list)
{
  update += " id = " + l.Id + " OR";
}

// To remove last OR
update.Remove(update.Length - 3);

MySqlHelper.ExecuteNonQuery("myConnectionString", update);

感觉很不安全,也很难看。

有更好的方法吗?

使用IN运算符效率会更高:

string update = "UPDATE table SET column = 0 WHERE id IN (";

foreach (Line l in list)
{
    update += l.Id + ",";
}

// To remove last comma
update.Remove(update.Length - 1);

// To insert closing bracket
update += ")";

是的,在 SQL 中,您有 'IN' 关键字,它允许您指定一组值。

这应该可以完成您想要的(语法可能有问题,但想法是存在的)

var ids = string.Join(',', list.Select(x => x.Id))

string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";

MySqlHelper.ExecuteNonQuery("myConnectionString", update);

但是,您执行 SQL 的方式可能被认为是危险的(您应该没问题,因为这看起来就像来自数据库的 ID,谁知道呢,安全总比后悔好)。在这里,您将参数直接传递到查询字符串中,这是 SQL 注入的潜在风险,非常危险。有很多方法可以解决这个问题,并使用内置的 .NET 'SqlCommand' 对象

https://www.w3schools.com/sql/sql_injection.asp

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0

如果使用 .NET Core Framework,请参阅以下内容 library,它为 WHERE IN 创建参数。该库是我 4.7 年前在 Framework 中编写的 VB.NET 的一个端口。克隆存储库,获取 SqlCoreUtilityLibrary 用于创建语句的项目。

设置.

public void UpdateExample()
{
    var identifiers = new List<int>() { 1, 3,20, 2,  45 };
    var (actual, exposed) = DataOperations.UpdateExample(
        "UPDATE table SET column = 0 WHERE id IN", identifiers);

    Console.WriteLine(actual);
    Console.WriteLine(exposed);
}

刚好足以创建参数化 SQL 语句的代码。注意 ActualCommandText method 用于开发,而不用于生产,因为它揭示了参数的实际值。

public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{

    using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
    using var cmd = new SqlCommand() { Connection = cn };
    
    cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);


    cmd.AddParamsToCommand("p", identifiers);

    return (cmd.CommandText, cmd.ActualCommandText());

}

For a real app all code would be done in the method above rather than returning the two strings.

结果

UPDATE table SET column = 0 WHERE id IN (@p0,@p1,@p2,@p3,@p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)