看来您必须使用动态 SQL,这基本上意味着您必须将表名连接到查询字符串中并通过 TSQL 中的“sp_executesql”存储过程运行它。
使用 SqlCommand 不是动态 SQL。尽管您正在动态构建 SQL 字符串,但最终您仍然在运行一个普通的旧 SQL 字符串。
为了安全地执行此操作并针对 SQL 注入进行项目,您必须确保表名有效,并且您必须通过任何必要的方式自己执行此操作。幸运的是,您有 3 个不错的选择,其中之一非常简单且几乎万无一失。
正如其他人所提到的,您可以:
- 根据白名单检查名称,或
- 在系统表中查找名称以查看它是否是真实的表名,或者
- 只需使用QUOTENAME 函数来转义字符并使它们成为带引号的标识符。
我有一个关于按名称删除表的类似问题,这就是提到 QUOTENAME 函数的地方:https ://stackoverflow.com/a/19528324/88409
要使用动态 SQL,您实际上必须执行以下操作:
string sql =
"declare @query nvarchar(max) = 'SELECT * FROM ' + QUOTENAME(@tablename) + ' WHERE ' + QUOTENAME(@columnname) + ' = @cv'; " +
"declare @params nvarchar(500) = N'@cv nvarchar(500)'; " +
"exec sp_executesql @query, @params, @cv=@columnvalue;";
SqlCommand command = new SqlCommand( sql, conn );
command.Parameters.AddWithValue( "@tablename", tableName );
command.Parameters.AddWithValue( "@columnname", columnName );
command.Parameters.AddWithValue( "@columnvalue", columnValue );
如果碰巧 SqlCommand 类不支持使用“declare”语句进行如此复杂的查询,那么您只需将“sql”字符串的值移动到采用相同三个参数的存储过程中,然后调用它通过将 SqlCommand.CommandType 设置为 StoredProcedure 来命名,如下所示:
SqlCommand command = new SqlCommand( "MyStoredProcedureName", conn );
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue( "@tablename", tableName );
command.Parameters.AddWithValue( "@columnname", columnName );
command.Parameters.AddWithValue( "@columnvalue", columnValue );