Est-il possible d’interroger un nom de colonne spécifié par l’utilisateur sans recourir à un SQL dynamic?

J’essaie d’implémenter une API avec la signature suivante:

public static List SearchDatabase( ssortingng column, ssortingng value); 

L’implémentation de l’API doit construire une requête SQL SELECT avec une clause WHERE qui utilise un nom de colonne spécifié dans le paramètre column .

La requête:

 ssortingng query = ssortingng.Format( "SELECT Name, @Column " + "FROM Db1 " + "INNER JOIN Db2 ON Db1.Id = Db2.Id " + "WHERE @Column = @Value"); 

La commande SQL et les parameters:

 SqlCommand selectCmd = new SqlCommand( query, connection); selectCmd.CommandTimeout = SqlCommandTimeout; selectCmd.Parameters.AddRange( new SqlParameter[] { new SqlParameter("@Column", column), new SqlParameter("@Value", value) }); 

Et je l’exécute comme ceci:

 SqlDataReader sqlDataReader = selectCmd.ExecuteReader(); while (sqlDataReader.Read()) { // ... } 

Le problème est que sqlDataReader ne renvoie aucune ligne, je ne vais donc pas dans la boucle while ci-dessus.

Toutefois, si je modifie la dernière ligne de la requête ci-dessus à partir de:

 "WHERE @Column = @Value"); 

à

 "WHERE Vendor = @Value"); 

(c.-à-d. coder en dur le nom de la colonne en ‘Vendor’)

D’après mes recherches, il est impossible de transmettre les noms de colonne en tant que parameters, mais uniquement les valeurs sur lesquelles nous interrogeons. Cependant, il semble que je @Column utiliser le paramètre @Column dans la clause SELECT , mais pas la clause WHERE .

Je ne veux pas recourir au SQL dynamic à cause des problèmes d’injection SQL. Y a-t-il une autre solution?

Malheureusement, les noms de table, les noms de colonne ne peuvent pas être paramétrés. Comme vous connaissez la structure de la table, vous pouvez définir une liste blanche de noms de colonnes possibles dans ce paramètre, puis utiliser une concaténation de chaînes pour éviter l’injection de code SQL:

 "WHERE " + Sanitize(column) + " = @Value"); 

Il n’y a rien de mal avec le SQL dynamic tant que vous échappez correctement le nom de la colonne:

 ssortingng query = ssortingng.Format( "SELECT Name, {0} " + "FROM Db1 " + "INNER JOIN Db2 ON Db1.Id = Db2.Id " + "WHERE {0} = @Value", Delimit(column)); 

où…

 public static ssortingng Delimit(ssortingng name) { if(name == null) { throw new ArgumentNullException("name"); } else if(name.Length == 0) { throw new ArgumentException("name"); } return "[" + name.Replace("]", "]]") + "]"; } 

Vous pouvez conserver les parameters et implémenter la sélection comme une instruction de casse correspondant à différentes valeurs pour le nom de colonne. Vous devrez peut-être atsortingbuer des valeurs si les colonnes sont de types différents.

  Select case when @column = 'UserName' then username when @column = 'Email' then email Else firstname End as Column From MyTable Where value = @vendor 

Cependant, je ne vois pas l’intérêt de cela. Il suffit de renvoyer toutes les colonnes et de choisir celle qui vous intéresse en utilisant le résultat défini en C # (si cela est réalisable).

Non, vous ne pouvez pas paramétrer les noms de colonne de cette manière, pas dans SQL Server.

Pour éviter de créer une requête dynamic, vous pouvez utiliser une expression CASE comme celle-ci:

 SELECT Name, CASE @Column WHEN 'Age' THEN Age WHEN 'Weight' THEN Weight … END … 

Notez cependant que les différents types de colonnes possibles doivent être implicitement compatibles entre eux dans ce cas (jeu de mots médiocre).

Vraisemblablement, le nom de la colonne a déjà été validé? Sinon, vous pouvez append une validation supplémentaire en ajoutant une select 1 from where = @column .

Si votre prédicat est toujours = vous n’avez pas besoin de sélectionner @Column . L’effort supplémentaire requirejs pour renseigner la structure de données côté client est sortingvial.

EDIT: Au lieu de valider le nom de la colonne à chaque appel, le commentaire de @ Darin dans “2011-09-16 07: 24: 34Z”, ici d’une initialisation unique, est préférable.