使用SqlParameter创建Order By子句

时间:2011-06-22 15:21:26

标签: c# asp.net sql-server

我试图将我对SQL语句中的变量的所有引用移动到SqlParameter类,但由于某种原因,此查询失败。

string orderBy = Request.QueryString["OrderBy"];
//Fix up the get vars
if (orderBy == null)
    orderBy = "name ASC";

string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY @OrderBy";
SqlCommand cmd = new SqlCommand(selectCommand, dataConnection);
cmd.Parameters.Add(new SqlParameter("@OrderBy", orderBy));

//Create the SQLDataAdapter instance
SqlDataAdapter dataCommand = new SqlDataAdapter(cmd);

//Create the DataSet instance
DataSet ds = new DataSet();
//Get data from a server and fill the DataSet  
dataCommand.Fill(ds);

这是错误

System.Data.SqlClient.SqlException:由ORDER BY编号1标识的SELECT项包含一个变量,作为标识列位置的表达式的一部分。只有在引用列名的表达式进行排序时,才允许使用变量。

这行失败了。

dataCommand.Fill(ds);

5 个答案:

答案 0 :(得分:14)

你真的有三种选择。

1)使用数据视图订购结果集

2)如果你知道可以订购的列,你可以测试字符串,然后使用然后选择订单。例如

例如,这将起作用

DECLARE @orderby varchar(255)
SET @orderby = 'Name ASC'

SELECT [Your Column here ]FROM sys.tables 
ORDER BY    
   case WHEN @orderby = 'Name ASC' Then name ELSE null END ASC,
   case WHEN @orderby = 'Name DESC' Then name ELSE null END DESC,
   CASE WHEN @orderby = 'Object_id ASC' then object_id ELSE null END ASC,
   CASE WHEN @orderby = 'Object_id DESC' then object_id ELSE null END DESC

3)最后一个选项与#2相同,但在C#代码中。请确保您不仅仅是从用户输入中添加ORDER BY子句,因为这对于SQL注入是有效的。

这是安全的,因为OrderBy Url参数"Name Desc; DROP table Users"将被忽略

string SafeOrderBy = "";
string orderBy = Request.QueryString["OrderBy"];
//Fix up the get vars
if (orderBy == null)
    orderBy = "name ASC";

if (orderby == "name Desc")
{
     SafeOrderBy == "name Desc"
}


string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY "
selectCommand  += SafeOrderBy ;

答案 1 :(得分:4)

使用SqlCommand是防止sql注入的方法。你改变顺序的方法与在这种情况下使用sql注入是一样的,所以它不应该被允许 - params用作常量,不能用作列名或表名。

你不必连接sortBy的内容,只需将其用作枚举,并根据其值连接你确定安全的东西。像这样:

If(orderBy == "some_column")
{
   selectColumn += "someColumn";
}
...

答案 2 :(得分:1)

我遇到了和你一样的问题,但是列出的解决方案无法使用,因为我的可能排序列是模型的属性之一,如果模型很大,那将意味着太多的if语句。 我对这个相关问题的解决方案是使用Reflection。类似的东西:

class MyModel {
    public string MyField1 { get; set; }
    public string MyField2 { get; set; }
    // ...
}

//...
using System.Reflection;
// sortBy = "MyField1"
// sortDirection = "Asc";
var sql = "SELECT FROM foo WHERE bar=baz ORDER BY ";
foreach (var prop in typeof(MyModel).GetProperties())
{
    if (sortBy.Equals(prop.Name))
    {
        sql += (prop.Name + (sortDirection.Value.Equals("Asc") ? " ASC" : " DESC"));
        break;
    }
}

此解决方案的好处是,无论我的模型如何更改,此代码都将支持按其任何属性进行排序,因此也不需要更改。

答案 3 :(得分:0)

我找到了一个如何执行此操作的示例here

您可以在CASE结构中定义不同的排序顺序,并将它们适当地执行到您的变量值:

  SELECT CompanyName,
         ContactName,
         ContactTitle

    FROM Customers

ORDER BY CASE WHEN @SortOrder = 1 THEN CompanyName
              WHEN @SortOrder = 2 THEN ContactName
         ELSE ContactTitle

我自己没有测试,但它可以工作。你可以尝试一下。一个明显的缺点是你必须编写所有的order-by语句。

答案 4 :(得分:-1)

你只是连接字符串。一种更简单的方法是:

string orderBy = "name ASC";
string selectCommand = "SELECT cat_id AS id, cat_name AS name FROM table_name ORDER BY " + orderBy;

我假设你正在这样做,因为你让调用者决定排序字段/方向,因此orderBy分开。

参数,作为倾斜提示的错误消息,将在WHERE子句中使用,例如, WHERE someColumn = @someValue等。