【问题标题】:String list in SqlCommand through Parameters in C#通过 C# 中的参数在 SqlCommand 中的字符串列表
【发布时间】:2010-09-10 03:41:50
【问题描述】:

在 C# 中使用 SqlCommand 我创建了一个查询,该查询在 where 子句中包含一个 IN (list...) 部分。而不是循环通过我的字符串列表生成查询所需的列表(如果您认为在 sqlInjection 中很危险)。我认为我可以创建一个像这样的参数:

SELECT blahblahblah WHERE blahblahblah IN @LISTOFWORDS

然后在代码中我尝试添加这样的参数:

DataTable dt = new DataTable();
dt.Columns.Add("word", typeof(string));
foreach (String word in listOfWords)
{
    dt.Rows.Add(word);
}
comm.Parameters.Add("LISTOFWORDS", System.Data.SqlDbType.Structured).Value = dt;

但这不起作用。

问题:

  • 我在尝试一些不可能的事情吗?
  • 我是否采取了错误的方法?
  • 我在这种方法中是否有错误?

感谢您的宝贵时间 :)

【问题讨论】:

    标签: c# sql sql-server


    【解决方案1】:

    您尝试做的事情是可能的,但不使用您当前的方法。这是在 SQL Server 2008 之前的所有可能的解决方案中非常常见的问题,这些解决方案在性能、安全性和内存使用方面进行了权衡。

    This link shows some approaches for SQL Server 2000/2005

    SQL Server 2008 supports passing a table value parameter.

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      您想考虑该列表的来源。通常,该信息位于数据库中的某处。例如,而不是这个:

      SELECT * FROM [Table] WHERE ID IN (1,2,3)
      

      您可以使用这样的子查询:

      SELECT * FROM [Table] WHERE ID IN ( SELECT TableID FROM [OtherTable] WHERE OtherTableID= @OtherTableID )
      

      【讨论】:

      • 不幸的是,该信息不在另一个表上。是一些数据收集的结果。但这是一个很好的方法,我会写下来以备将来使用。谢谢
      【解决方案3】:

      【讨论】:

      • 第一个链接谈到了一个表变量。这是我用 DataTable 创建的。但在这种情况下,我不能让 mssql 吞下它。我想这个想法是要创建一个表 guid,word。在那里插入,然后按 guid 删除。
      【解决方案4】:
      • 我在尝试一些不可能的事情吗?

      不,这不是不可能的。

      • 我是否采取了错误的方法?

      您的方法不起作用(至少在 .net 2 中)

      • 我在这种方法中是否有错误?

      如果可能的话,我会尝试“Joel Coehoorn”解决方案(第二个答案)。 否则,另一种选择是发送一个“字符串”参数,其中所有值都由分隔符分隔。编写一个动态查询(根据字符串中的值构建它)并使用“exec”执行它。

      另一种解决方案是直接从代码构建查询。是这样的:

      StringBuilder sb = new StringBuilder();
      for (int i=0; i< listOfWords.Count; i++)
      {
          sb.AppendFormat("p{0},",i);
          comm.Parameters.AddWithValue("p"+i.ToString(), listOfWords[i]);
      }
      
      comm.CommandText = string.Format(""SELECT blahblahblah WHERE blahblahblah IN ({0})", 
      sb.ToString().TrimEnd(','));
      

      命令应该是这样的:

      SELECT blah WHERE blah IN (p0,p1,p2,p3...)...p0='aaa',p1='bbb'
      

      在 MsSql2005 中,“IN”仅适用于 256 个值。

      【讨论】:

      • 在底部查询中,第一次出现 p0 时出现错误。
      【解决方案5】:

      我建议将参数设置为以逗号分隔的值字符串,并在 SQL 中使用拆分函数将其转换为值的单列表,然后您可以使用 IN 功能。

      http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - 拆分函数

      【讨论】:

      • 这很容易发生 Sql Injection :(
      【解决方案6】:

      如果您想将列表作为字符串传递给参数,您可以动态构建查询。

      声明@query varchar(500) SET @query = 'SELECT blah blah WHERE blahblah in (' + @list + ')' 执行(@query)

      【讨论】:

        【解决方案7】:

        我曾经遇到过同样的问题,我认为现在可以通过 ADO.NET API 直接解决此问题。

        您可能会考虑将单词插入到一个临时表中(加上一个 queryid 或其他内容),然后从查询中引用该临时表。或者动态创建查询字符串并通过其他措施(例如正则表达式检查)避免 sql 注入。

        【讨论】:

        • 我将使用第一个选项 + 批量 sql 插入。也许这可以为我节省一些 SQL I/O
        【解决方案8】:

        这是一个老问题,但我想出了一个优雅的解决方案,我喜欢重复使用它,我认为其他人都会发现它很有用。

        首先,您需要在 SqlServer 中创建一个 FUNCTION,它接受一个分隔输入并返回一个表,其中项目被拆分为记录。

        下面是代码:

        ALTER FUNCTION [dbo].[Split]
        (
            @RowData nvarchar(max),
            @SplitOn nvarchar(5) = ','
        )  
        RETURNS @RtnValue table 
        (
            Id int identity(1,1),
            Data nvarchar(100)
        ) 
        AS  
        BEGIN 
            Declare @Cnt int
            Set @Cnt = 1
        
            While (Charindex(@SplitOn,@RowData)>0)
            Begin
                Insert Into @RtnValue (data)
                Select 
                    Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))
        
                Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
                Set @Cnt = @Cnt + 1
            End
        
            Insert Into @RtnValue (data)
            Select Data = ltrim(rtrim(@RowData))
        
            Return
        END
        

        您现在可以执行以下操作:

        Select Id, Data from dbo.Split('123,234,345,456',',')
        

        不用担心,这不会受到 Sql 注入攻击。

        接下来编写一个使用逗号分隔数据的存储过程,然后您可以编写一个使用此拆分函数的 sql 语句:

        CREATE PROCEDURE [dbo].[findDuplicates]
            @ids nvarchar(max)
        as
        begin
            select ID
              from SomeTable with (nolock)
             where ID in (select Data from dbo.Split(@ids,','))
        end
        

        现在您可以围绕它编写一个 C# 包装器:

        public void SomeFunction(List<int> ids)
        {
            var idsAsDelimitedString = string.Join(",", ids.Select(id => id.ToString()).ToArray());
        
            // ... or however you make your connection
            var con = GetConnection();
        
            try
            {
                con.Open();
        
                var cmd = new SqlCommand("findDuplicates", con);
        
                cmd.Parameters.Add(new SqlParameter("@ids", idsAsDelimitedString));
        
                var reader = cmd.ExecuteReader();
        
                // .... do something here.
        
            }
            catch (Exception)
            {
                // catch an exception?
            }
            finally
            {
                con.Close();
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2012-05-06
          • 1970-01-01
          • 2011-07-19
          • 2011-12-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-10
          • 1970-01-01
          • 2019-12-27
          相关资源
          最近更新 更多