SqlCommand中的字符串列表通过C#中的参数
在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;
但这不起作用。
问题:
- 我在尝试一些不可能的事吗
- 我采取了错误的做法吗?
- 这种方法我有错吗?
谢谢你的时间 :)
您尝试做的是可能但不使用您当前的方法。 这是SQL Server 2008在与性能,安全性和内存使用相关的折衷之前所有可能的解决方案的一个非常常见的问题。
此链接显示了SQL Server 2000/2005的一些方法
SQL Server 2008支持传递表值参数。
我希望这有帮助。
您想要考虑该列表的来源。 通常,该信息在某个地方的数据库中。 例如,而不是这样:
SELECT * FROM [Table] WHERE ID IN (1,2,3)
您可以使用这样的子查询:
SELECT * FROM [Table] WHERE ID IN ( SELECT TableID FROM [OtherTable] WHERE OtherTableID= @OtherTableID )
如果我理解正确,您正在尝试将列表作为SQL参数传递。
有些人以前尝试过这种方法,取得了有限的成功:
将数组传递给存储过程
SQL 2005中的数组和列表
在没有字符串操作的情况下将值数组传递给SQL Server
使用MS SQL 2005的XMLfunction将值列表传递给命令
- 我在尝试一些不可能的事吗
不,这不是不可能的。
- 我采取了错误的做法吗?
你的方法不起作用(至少在.net 2中)
- 这种方法我有错吗?
如果可能的话,我会尝试“Joel Coehoorn”解决方案(第2个答案)。 否则,另一个选项是发送“string”参数,其中包含由分隔符分隔的所有值。 编写动态查询(根据字符串中的值构建它)并使用“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个值。
我建议将参数设置为逗号分隔的值字符串,并使用SQL中的Split函数将其转换为单个列值表,然后您可以使用INfunction。
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 – 分割function
如果要将列表作为参数中的字符串传递,则可以动态构建查询。
DECLARE @query varchar(500)SET @query =’SELECT blah blah WHERE blahblah in(’+ @ list +’)’EXECUTE(@query)
我曾经遇到过同样的问题,我认为现在可以通过ADO.NET API直接执行此操作。
您可以考虑将单词插入temptable(加上queryid或其他内容),然后从查询中引用该temptable。 或者动态创建查询字符串并通过其他措施(例如正则表达式检查)避免sql注入。
这是一个古老的问题,但我想出了一个优雅的解决方案,我喜欢重用,我想其他人都会发现它很有用。
首先,您需要在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注入攻击的影响。
接下来编写一个存储过程,该过程采用逗号分隔的数据,然后您可以编写使用此Split函数的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 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(); } }