I am trying to implement an API with the following signature:
public static List<string> SearchDatabase(
string column,
string value);
The implementation of the API needs to construct an SQL SELECT query with a WHERE clause that uses a column name specified in the column parameter.
The query:
string query =
string.Format(
"SELECT Name, @Column " +
"FROM Db1 " +
"INNER JOIN Db2 ON Db1.Id = Db2.Id " +
"WHERE @Column = @Value");
The SQL command and parameters:
SqlCommand selectCmd =
new SqlCommand(
query,
connection);
selectCmd.CommandTimeout = SqlCommandTimeout;
selectCmd.Parameters.AddRange(
new SqlParameter[]
{
new SqlParameter("@Column", column),
new SqlParameter("@Value", value)
});
And I execute it like this:
SqlDataReader sqlDataReader = selectCmd.ExecuteReader();
while (sqlDataReader.Read())
{
// ...
}
The problem is that sqlDataReader doesn’t return any rows, so I don’t go into the while loop above.
However, if I change the last line of the query above from:
"WHERE @Column = @Value");
to
"WHERE Vendor = @Value");
(i.e. hardcode the column name to ‘Vendor’) then it works.
My understanding from research I have done is that it’s not possible to pass column names as parameters, but only values we are querying on. But, it does appear to let me use the @Column parameter in the SELECT clause, just not the WHERE clause.
I don’t want to resort to dynamic SQL because of the issues with SQL injection. Is there another way around this?
Unfortunately table names, column names cannot be parametrized. Because you know the structure of the table you could have a whitelist of possible column names in this parameter and then use string concatenation for it to avoid SQL injection: