是否可以在不诉诸动态SQL的情况下查询用户指定的列名?

Leo*_*Hat 2 c# sql sql-server-2005

我正在尝试使用以下签名实现API:

public static List<string> SearchDatabase(
    string column,
    string value);
Run Code Online (Sandbox Code Playgroud)

API的实现需要构造一个SQL SELECT查询,其WHERE子句使用column参数中指定的列名.

查询:

string query =
    string.Format(
        "SELECT Name, @Column " +
        "FROM Db1 " +
        "INNER JOIN Db2 ON Db1.Id = Db2.Id " +
        "WHERE @Column = @Value");
Run Code Online (Sandbox Code Playgroud)

SQL命令和参数:

SqlCommand selectCmd =
    new SqlCommand(
        query,
        connection);
selectCmd.CommandTimeout = SqlCommandTimeout;
selectCmd.Parameters.AddRange(
    new SqlParameter[]
{
    new SqlParameter("@Column", column),
    new SqlParameter("@Value", value)
});
Run Code Online (Sandbox Code Playgroud)

我执行它是这样的:

SqlDataReader sqlDataReader = selectCmd.ExecuteReader();
while (sqlDataReader.Read())
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

问题是sqlDataReader不返回任何行,所以我不进入while上面的循环.

但是,如果我更改上面查询的最后一行:

"WHERE @Column = @Value");
Run Code Online (Sandbox Code Playgroud)

"WHERE Vendor = @Value");
Run Code Online (Sandbox Code Playgroud)

(即将列名硬编码为'Vendor')然后它可以工作.

我从研究中得到的理解是,不可能将列名作为参数传递,而只传递我们要查询的值.但是,它确实让我使用@ColumnSELECT子句中的参数,而不是WHERE子句.

由于SQL注入的问题,我不想求助于动态SQL.还有另一种方法吗?

Dar*_*rov 7

不幸的是,表名,列名不能参数化.因为您知道表的结构,所以您可以在此参数中使用可能列名称的白名单,然后使用字符串连接来避免SQL注入:

"WHERE " + Sanitize(column) + " = @Value");
Run Code Online (Sandbox Code Playgroud)

  • @LeopardSkinPillBoxHat,你可以使用ADO.NET查询你的数据库一次(例如在应用程序启动时),以便读取表的模式并获取它的所有可能的列名,并在整个生命周期内存储该白名单. Sanitize函数将使用的应用程序. (3认同)