2008-09-17 5 views
6

Utilisation d'une commande SqlCommand en C# J'ai créé une requête qui contient une partie IN (liste ...) dans la clause where. Au lieu de boucler dans ma liste de chaînes générant la liste dont j'ai besoin pour la requête (dangereuse si vous pensez dans sqlInjection). Je pensais que je pouvais créer un paramètre comme:Liste de chaînes dans SqlCommand via les paramètres de C#

SELECT blahblahblah WHERE blahblahblah IN @LISTOFWORDS 

Ensuite, dans le code que j'essaie d'ajouter un paramètre comme celui-ci:

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; 

Mais cela ne fonctionne pas.

Questions:

  • Suis-je essayer quelque chose d'impossible?
  • Ai-je pris la mauvaise approche?
  • Est-ce que j'ai des erreurs dans cette approche?

Merci pour votre temps :)

Répondre

3

Vous voulez penser à où cette liste vient. Généralement, cette information est dans la base de données quelque part. Par exemple, au lieu de ceci:

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

Vous pouvez utiliser un sous-requête comme ceci:

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

Malheureusement, les informations ne sont pas sur un autre tableau. Est le résultat de la collecte de données. Mais c'est une bonne approche que je vais écrire pour des utilisations futures. Merci – graffic

0

je recommande la définition du paramètre comme une chaîne délimitée par des virgules des valeurs et utiliser une fonction Split, dans SQL transformez cela en une seule table de valeurs de colonnes et vous pouvez ensuite utiliser la fonction IN.

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - Fonctions de Split

+0

C'est assez Sql Injection prone :( – graffic

1

Si je comprends bien, vous essayez de passer une liste en tant que paramètre SQL.

Certaines personnes ont tenté auparavant avec un succès limité:

Passing Arrays to Stored Procedures

Arrays and Lists in SQL 2005

Passing Array of Values to SQL Server without String Manipulation

Using MS SQL 2005's XML capabilities to pass a list of values to a command

+0

Le premier lien parle d'une variable de table.Ceci est ce que j'ai créé avec le DataTAble.Mais dans ce cas, je ne peux pas faire mssql pour l'avaler Je suppose que l'idée est va être créer un guid de table, mot.Insérer là, puis delt par guid – graffic

0

Si vous voulez passer la liste en tant que chaîne dans un paramètre, vous pouvez simplement construire la requête de manière dynamique .

DECLARE @query varchar (500) SET @query = 'SELECT bla bla OÙ blahblah dans (' + @list + ') EXECUTE (@query)

+1

Quite SQL Injection sujettes :(Mais merci :) – graffic

0

je l'habitude d'avoir le même problème, Je pense qu'il est maintenant possible de faire cela directement sur l'API ADO.NET.

Vous pourriez envisager d'insérer les mots dans une table de la tentation (plus un queryid ou quelque chose) et ensuite se référer à cette tenttable à partir de la requête. Ou créez dynamiquement la chaîne de requête et évitez l'injection SQL par d'autres mesures (par exemple, les vérifications de regex).Ce que vous essayez de faire est possible mais n'utilise pas votre approche actuelle.

+0

Je vais aller avec la première option + un insert sql en vrac.Cela peut peut-être me sauver un peu de SQL E/S – graffic

1
  • Est-ce que j'essaie quelque chose d'impossible?

Non, ce n'est pas impossible.

  • Ai-je pris la mauvaise approche?

Votre approche ne fonctionne pas (au moins en .net 2)

  • Ai-je des erreurs dans cette approche?

Je voudrais essayer "Joel Coehoorn" solution (2ème réponse) si c'est possible. Sinon, une autre option consiste à envoyer un paramètre "chaîne" avec toutes les valeurs délimitées par un séparateur. Ecrire une requête dynamique (la construire en fonction des valeurs de la chaîne) et l'exécuter en utilisant "exec".

Une autre solution sera de construire la requête directement à partir du code. Somthing comme ceci:

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(',')); 

La commande doit ressembler à:

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

En MSSQL2005, "IN" fonctionne uniquement avec 256 valeurs.

+0

Dans la requête du bas, j'obtiens une erreur sur la première occurrence de p0. – micahhoover

0

C'est une vieille question mais j'ai trouvé une solution élégante pour cela que j'aime réutiliser et je pense que tout le monde la trouvera utile.

Tout d'abord vous devez créer un FUNCTION dans SqlServer qui prend une entrée délimitée et retourne une table avec les éléments divisés en enregistrements.

Voici le code suivant pour cela:

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 

Vous pouvez maintenant faire quelque chose comme ceci:

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

Ne craignez pas, cela ne peut pas être sensible aux attaques par injection Sql.

suivant écrire une procédure stockée qui prend vos données séparées par des virgules et vous pouvez écrire une instruction SQL qui utilise cette fonction Split:

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 

Maintenant, vous pouvez écrire un wrapper C# autour:

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(); 
    } 
} 
Questions connexes