【问题标题】:SqlParameter and IN statement [duplicate]SqlParameter 和 IN 语句 [重复]
【发布时间】:2013-11-22 10:49:03
【问题描述】:

我需要以下查询:

createList(string commaSeparatedElements) {
    ...
    SqlCommand query = new SqlCommand("SELECT * FROM table WHERE id IN ("+commaSeparatedElements+")");
    ...
}

我想使用参数化查询来编写它,因此检查字符串中的每个元素以防止 Sql-Injections。

伪代码:

createList(string commaSeparatedElements) {
    ...
    SqlParameterList elements = new SqlParameterList("@elements", SqlDbType.Int);
    SqlParameterList.Values = commaSeparatedElements.split(new Char[1] {','});
    SqlCommand query = new SqlCommand("SELECT * FROM table WHERE id IN (@elements)");
    query.Parameters.Add(elements);
    ...
}

C#中是否存在类似的东西,还是我必须自己编写?

编辑:感谢所有答案。当我尽量不使用我不理解的代码时(过去几天有太多糟糕的经历),dapper 和 table-valued 参数,即使它们可能非常适合我的需求,也是禁止使用的。我刚刚做了一个循环。

string[] elements = commaSeparatedElements.split(new Char[1] {','});
StringList idParamList = new StringList();
for(int i=0;i<elements.Count;i++) {
    query.Parameters.AddWithValue("@element"+i,Convert.ToInt32(elements[i]));
    idParamList.Add("@element" + i);
}
SqlCommand query = new SqlCommand("SELECT * FROM table WHERE id IN ("+String.Join(",",idParamList)+")");

【问题讨论】:

标签: c# sql sql-server


【解决方案1】:

"dapper"对此有一些巫术:

var ids = new List<int> {1,2,3,4,5};
var rows = conn.Query<SomeType>("select * from table where id in @ids",
      new { ids }).ToList();

您会注意到in 用法中缺少括号; “dapper”发现了这一点,并自动执行参数扩展。此外,它还为您处理所有命令细节,包括将数据映射回SomeType 实例。

在上面的例子中,它将扩展为(大约)等价于:

int ids0 = 1, ids1 = 2, ids2 = 3, ids3 = 4, ids4 = 5;
var rows = conn.Query<SomeType>(
    "select * from table where id in (@ids0,@ids1,@ids2,@ids3,@ids4)",
      new { ids0, ids1, ids2, ids3, ids4 }).ToList();

【讨论】:

    【解决方案2】:

    SQL Server 有一个数据结构,非常适合保存相同“类型”的多个值 - 这就是一个表。

    值得庆幸的是,近年来,他们引入了Table-Valued Parameters,以便您可以在客户端代码中构建一个表并以自然的方式将其传递给 SQL Server,例如:

    // Assumes connection is an open SqlConnection.
    using (connection)
    {
    // Create a DataTable with the modified rows.
    DataTable addedCategories = CategoriesDataTable.GetChanges(
        DataRowState.Added);
    
    // Define the INSERT-SELECT statement.
    string sqlInsert = 
        "INSERT INTO dbo.Categories (CategoryID, CategoryName)"
        + " SELECT nc.CategoryID, nc.CategoryName"
        + " FROM @tvpNewCategories AS nc;"
    
    // Configure the command and parameter.
    SqlCommand insertCommand = new SqlCommand(
        sqlInsert, connection);
    SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
        "@tvpNewCategories", addedCategories);
    tvpParam.SqlDbType = SqlDbType.Structured;
    tvpParam.TypeName = "dbo.CategoryTableType";
    
    // Execute the command.
    insertCommand.ExecuteNonQuery();
    }
    

    【讨论】:

    • 这是最好的方法,但需要注意的是:由于参数嗅探,TVP 可能会使用存储过程。
    【解决方案3】:

    如果你声明

    List<int> Ids = new List<int>();
    

    并在那里添加你想要的 id。很容易将其转换为逗号分隔的列表,如下所示

    string listOfIds = string.Join(",", Ids.Select(Id => Id.ToString()).ToArray());
    

    然后将此字符串作为参数传递给参数化查询。

    【讨论】:

    • 行不通,因为我不问SELECT * FROM table WHERE id IN ("1,2,3"),我问SELECT * FROM table WHERE id IN (1,2,3)
    【解决方案4】:

    我使用这样的辅助方法

    /// <summary>
    /// Adds a sequence of parameters to an existing parameter collection
    /// </summary>
    /// <typeparam name="T">Type of parameter values</typeparam>
    /// <param name="parameters">Existing parameter collection</param>
    /// <param name="pattern">Name pattern of parameters. Must be a valid <see langword="int"/> format string</param>
    /// <param name="parameterType">Database type of parameters</param>
    /// <param name="length">Length of parameter. 0 for numeric parameters</param>
    /// <param name="values">Sequence of values</param>
    /// <returns>Comma separated string of parameter names</returns>
    public static string AddParameters<T>(SqlParameterCollection parameters,
                                          string pattern,
                                          SqlDbType parameterType,
                                          int length,
                                          IEnumerable<T> values) {
        if (parameters == null)
            throw new ArgumentNullException("parameters");
        if (pattern == null)
            throw new ArgumentNullException("pattern");
        if (values == null)
            throw new ArgumentNullException("values");
        if (!pattern.StartsWith("@", StringComparison.CurrentCultureIgnoreCase))
            throw new ArgumentException("Pattern must start with '@'");
    
        var parameterNames = new List<string>();
        foreach (var item in values) {
            var parameterName = parameterNames.Count.ToString(pattern, CultureInfo.InvariantCulture);
            parameterNames.Add(parameterName);
            parameters.Add(parameterName, parameterType, length).Value = item;
        }
    
        return string.Join(",", parameterNames.ToArray());
    }
    

    这样使用

    string sql = "SELECT col1 " +
                 "FROM Table " +
                 "WHERE col2 IN ({@Values}) ";
    var paramNames = SqlHelper.AddParameters(command.Parameters,
                                             "@Value0",
                                             SqlDbType.Int,
                                             0,
                                             listOfInts);
    command.CommandText = sql.Replace("{@Values}", paramNames);
    ...
    

    (结果是类似SELECT ... IN (@Value0, @Value1, @Value2)的查询)

    【讨论】:

      【解决方案5】:

      您使用的是哪个版本的 sql server? 如果您使用的是 sql server 2008 或更高版本,则应尝试使用表值参数

      查看http://www.codeproject.com/Articles/39161/C-and-Table-Value-Parameters

      Pass table valued parameter using ADO.Net

      【讨论】:

        【解决方案6】:

        这是我在一个项目中所做的:

                StringBuilder sb = new StringBuilder("Id IN(");
                List<SqlParameter> parameters = new List<SqlParameter>();
                int i = 1;
        
                foreach (int item in items)
                {
                    string currentItem = "@Item" + i++.ToString();
                    sb.Append(currentItem + ",");
                    parameters.Add(new SqlParameter(currentItem , item));
                }
                sb.Remove(sb.Length-1, 1);
        
                sb.Append(")");
        

        希望对你有帮助

        【讨论】:

          【解决方案7】:

          或者,您可以为参数设置一个 if 条件。例如:

          if(commaSeparatedElements.Contains("'"))
             return;
          else
             //Pass parameter & Execute query 
          

          【讨论】:

            猜你喜欢
            • 2011-02-10
            • 1970-01-01
            • 2015-09-07
            • 2017-07-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-10
            • 1970-01-01
            相关资源
            最近更新 更多