Que se passe-t-il ?
Vous citez les listes de paramètres pour plusieurs surcharges de Add
. Ce sont des méthodes pratiques qui correspondent directement aux surcharges du constructeur pour le SqlParameter
classe. Ils construisent essentiellement l'objet paramètre en utilisant n'importe quel constructeur ayant la même signature que la méthode pratique que vous avez appelée, puis appellent SqlParameterCollection.Add(SqlParameter)
comme ceci :
SqlParameter foo = new SqlParameter(parameterName, dbType, size);
this.Add(foo);
AddWithValue
est similaire mais va encore plus loin dans la commodité, en définissant également la valeur. Cependant, il a en fait été introduit pour résoudre un défaut du framework. Pour citer MSDN,
La surcharge de
Add
qui prend une chaîne et un objet a été déconseillé en raison d'une éventuelle ambiguïté avec leSqlParameterCollection.Add
surcharge qui prend uneString
et unSqlDbType
valeur d'énumération où le passage d'un entier avec la chaîne peut être interprété comme étant soit la valeur du paramètre, soit leSqlDbType
correspondant valeur. UtilisezAddWithValue
chaque fois que vous souhaitez ajouter un paramètre en spécifiant son nom et sa valeur.
Le constructeur surcharge pour le SqlParameter
class sont de simples commodités pour définir les propriétés d'instance. Ils raccourcissent le code, avec un impact marginal sur les performances :le constructeur peut contourner les méthodes setter et opérer directement sur les membres privés. S'il y a une différence, ce ne sera pas beaucoup.
Que dois-je faire ?
Notez ce qui suit (à partir de MSDN)
Pour les paramètres bidirectionnels et de sortie, et les valeurs de retour, vous devez définir la valeur de
Size
. Ceci n'est pas requis pour les paramètres d'entrée, et s'il n'est pas explicitement défini, la valeur est déduite de la taille réelle du paramètre spécifié lorsqu'une instruction paramétrée est exécutée.
Le type par défaut est entrée. Cependant, si vous autorisez la taille à être déduite comme ceci et que vous recyclez l'objet paramètre dans une boucle (vous avez dit que vous étiez préoccupé par les performances), la taille sera définie par la première valeur et toutes les valeurs suivantes qui sont plus longues seront coupé. Évidemment, cela n'est significatif que pour les valeurs de longueur variable telles que les chaînes.
Si vous passez le même paramètre logique à plusieurs reprises dans une boucle, je vous recommande de créer un objet SqlParameter en dehors de la boucle et de le dimensionner de manière appropriée. Le surdimensionnement d'un varchar est inoffensif, donc s'il s'agit d'un PITA pour obtenir le maximum exact, définissez-le simplement plus grand que prévu pour la colonne. Parce que vous recyclez l'objet plutôt que d'en créer un nouveau pour chaque itération, la consommation de mémoire pendant la durée de la boucle va probablement baisser même si vous êtes un peu excité par le surdimensionnement.
À vrai dire, à moins que vous ne traitiez des milliers d'appels, rien de tout cela ne fera beaucoup de différence. AddWithValue
crée un nouvel objet, contournant le problème de dimensionnement. C'est court et doux et facile à comprendre. Si vous parcourez des milliers, utilisez mon approche. Si vous ne le faites pas, utilisez AddWithValue
pour garder votre code simple et facile à maintenir.
2008 c'était il y a longtemps
Depuis que j'ai écrit ceci, le monde a changé. Il y a de nouveaux types de dates, et il y a aussi un problème qui ne m'est pas venu à l'esprit jusqu'à ce qu'un problème récent avec les dates me fasse réfléchir aux implications de l'élargissement.
L'élargissement et le rétrécissement, pour ceux qui ne connaissent pas ces termes, sont des qualités des conversions de types de données. Si vous affectez un int à un double, il n'y a pas de perte de précision car double est "plus large". Il est toujours sûr de le faire, donc la conversion est automatique. C'est pourquoi vous pouvez attribuer un int à un double, mais dans l'autre sens, vous devez effectuer un cast explicite - double to int est une conversion restrictive avec une perte potentielle de précision.
Cela peut s'appliquer aux chaînes :NVARCHAR est plus large que VARCHAR, vous pouvez donc attribuer un VARCHAR à un NVARCHAR, mais l'inverse nécessite un transtypage. La comparaison fonctionne car le VARCHAR s'élargit implicitement en NVARCHAR, mais cela interférera avec l'utilisation des index !
Les chaînes C# sont Unicode, donc AddWithValue produira un paramètre NVARCHAR. À l'autre extrémité, les valeurs de colonne VARCHAR s'élargissent à NVARCHAR à des fins de comparaison. Cela n'arrête pas l'exécution de la requête mais empêche l'utilisation des index. C'est mauvais.
Que peux-tu y faire? Vous avez deux solutions possibles.
- Tapez explicitement le paramètre. Cela signifie qu'il n'y a plus d'AddWithValue
- Remplacez tous les types de colonnes de chaîne par NVARCHAR.
Abandonner VARCHAR est probablement la meilleure idée. Il s'agit d'un changement simple aux conséquences prévisibles et qui améliore votre histoire de localisation. Cependant, vous n'avez peut-être pas cette option.
Ces jours-ci, je ne fais pas beaucoup d'ADO.NET direct. Linq2Sql est maintenant mon arme de choix, et le fait d'écrire cette mise à jour m'a amené à me demander comment il gère ce problème. J'ai un désir soudain et brûlant de vérifier mon code d'accès aux données pour une recherche via les colonnes VARCHAR.
2019 et le monde a recommencé
Linq2Sql n'est pas disponible dans dotnet Core, je me retrouve donc à utiliser Dapper. Le problème [N]VARCHAR est toujours d'actualité, mais il n'est plus si enterré. Je pense que l'on peut aussi utiliser ADO, donc la boucle est bouclée à cet égard.