我正在尝试使用以下签名实现API:
public static List<string> SearchDatabase(
string column,
string value);
API的实现需要使用SELECT
子句构造SQL WHERE
查询,该子句使用column
参数中指定的列名。
查询:
string query =
string.Format(
"SELECT Name, @Column " +
"FROM Db1 " +
"INNER JOIN Db2 ON Db1.Id = Db2.Id " +
"WHERE @Column = @Value");
SQL命令和参数:
SqlCommand selectCmd =
new SqlCommand(
query,
connection);
selectCmd.CommandTimeout = SqlCommandTimeout;
selectCmd.Parameters.AddRange(
new SqlParameter[]
{
new SqlParameter("@Column", column),
new SqlParameter("@Value", value)
});
我按照这样执行:
SqlDataReader sqlDataReader = selectCmd.ExecuteReader();
while (sqlDataReader.Read())
{
// ...
}
问题是sqlDataReader
没有返回任何行,所以我不会进入上面的while
循环。
但是,如果我从上面更改上面查询的最后一行:
"WHERE @Column = @Value");
到
"WHERE Vendor = @Value");
(即将列名硬编码为'Vendor')然后它可以工作。
我从研究中得到的理解是,不可能将列名作为参数传递,而只传递我们要查询的值。但是,它似乎让我在@Column
子句中使用SELECT
参数,而不是WHERE
子句。
由于SQL注入问题,我不想求助于动态SQL。还有另一种解决方法吗?
答案 0 :(得分:7)
不幸的是,表名,列名不能参数化。因为您知道表的结构,所以可以在此参数中使用可能列名称的白名单,然后使用字符串连接来避免SQL注入:
"WHERE " + Sanitize(column) + " = @Value");
答案 1 :(得分:1)
只要正确转义列名,动态SQL就没有问题:
string query = string.Format(
"SELECT Name, {0} " +
"FROM Db1 " +
"INNER JOIN Db2 ON Db1.Id = Db2.Id " +
"WHERE {0} = @Value",
Delimit(column));
其中...
public static string Delimit(string name) {
if(name == null) {
throw new ArgumentNullException("name");
} else if(name.Length == 0) {
throw new ArgumentException("name");
}
return "[" + name.Replace("]", "]]") + "]";
}
答案 2 :(得分:1)
您可以保留参数并将select实现为匹配列名的不同值的case语句。如果列的类型不同,则可能需要转换值。
Select case
when @column = 'UserName' then username
when @column = 'Email' then email
Else firstname End as Column
From MyTable
Where value = @vendor
但是,我没有看到这一点,只返回所有列并使用C#中的结果集选择您感兴趣的列(如果可行的话)。
答案 3 :(得分:0)
不,你不能像这样参数化列名,而不是SQL Server。
要避免构建动态查询,可以使用如下的CASE表达式:
SELECT
Name,
CASE @Column
WHEN 'Age' THEN Age
WHEN 'Weight' THEN Weight
…
END
…
但请注意,在这种情况下,各种可能的列类型应该隐式地相互兼容(预期的双关语很差)。
答案 4 :(得分:0)
据推测,列名已经过验证?如果没有,您可以通过添加select 1 from <column-names-table> where <column-name-column> = @column
添加额外的验证。
如果您的谓词始终为=
,那么您无需选择@Column
。填充客户端数据结构所需的额外工作是微不足道的。
编辑:@ Darin对一次初始化的“2011-09-16 07:24:34Z”here的评论不是在每次调用时验证列名,而是更好。