public static class DataRecordExtensions
{
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i=0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
L'utilisation de Exception
s pour la logique de contrôle comme dans certaines autres réponses est considérée comme une mauvaise pratique et a des coûts de performance. Il envoie également des faux positifs au profileur de # exceptions levées et Dieu aide toute personne configurant son débogueur à casser les exceptions levées.
GetSchemaTable () est également une autre suggestion dans de nombreuses réponses. Ce ne serait pas un moyen préféré de vérifier l'existence d'un champ car il n'est pas implémenté dans toutes les versions (il est abstrait et lève NotSupportedException dans certaines versions de dotnetcore). GetSchemaTable est également trop performant en termes de performances car c'est une fonction assez lourde si vous consultez la source .
La boucle à travers les champs peut avoir un petit impact sur les performances si vous l'utilisez beaucoup et vous pouvez envisager de mettre en cache les résultats.
Il est préférable d'utiliser cette fonction booléenne:Un appel - sans exception. Cela pourrait lever des exceptions en interne, mais je ne pense pas.REMARQUE: dans les commentaires ci-dessous, nous avons compris cela ... le code correct est en fait le suivant:
la source
Je pense que votre meilleur pari est d'appeler GetOrdinal ("columnName") sur votre DataReader à l'avant, et d'attraper une IndexOutOfRangeException au cas où la colonne n'est pas présente.
En fait, faisons une méthode d'extension:
Éditer
Ok, ce post commence à recueillir quelques votes négatifs récemment, et je ne peux pas le supprimer car c'est la réponse acceptée, donc je vais le mettre à jour et (j'espère) essayer de justifier l'utilisation de la gestion des exceptions comme contrôler le flux.
L'autre moyen d'y parvenir, tel que publié par Chad Grant , est de parcourir chaque champ dans le DataReader et de faire une comparaison insensible à la casse pour le nom de champ que vous recherchez. Cela fonctionnera très bien et, à vrai dire, fonctionnera probablement mieux que ma méthode ci-dessus. Certes, je n'utiliserais jamais la méthode ci-dessus dans une boucle où performace était un problème.
Je peux penser à une situation dans laquelle la méthode try / GetOrdinal / catch fonctionnera là où la boucle ne fonctionne pas. C'est, cependant, une situation complètement hypothétique en ce moment donc c'est une justification très fragile. Quoi qu'il en soit, supportez-moi et voyez ce que vous en pensez.
Imaginez une base de données qui vous permettait de "créer un alias" dans une table. Imaginez que je puisse définir une table avec une colonne appelée "EmployeeName" mais aussi lui donner un alias de "EmpName", et faire une sélection pour l'un ou l'autre nom retournerait les données dans cette colonne. Jusqu'à présent avec moi?
Imaginez maintenant qu'il existe un fournisseur ADO.NET pour cette base de données, et qu'ils ont codé une implémentation IDataReader pour celle-ci qui prend en compte les alias de colonne.
Maintenant,
dr.GetName(i)
(comme utilisé dans la réponse de Chad) ne peut renvoyer qu'une seule chaîne, il ne doit donc renvoyer qu'un seul des "alias" sur une colonne. cependant,GetOrdinal("EmpName")
pourrait utiliser l'implémentation interne des champs de ce fournisseur pour vérifier l'alias de chaque colonne pour le nom que vous recherchez.Dans cette hypothétique situation de "colonnes aliasées", la méthode try / GetOrdinal / catch serait le seul moyen d'être sûr de vérifier chaque variation du nom d'une colonne dans l'ensemble de résultats.
Fragile? Sûr. Mais ça vaut le coup. Honnêtement, je préfère de loin une méthode HasColumn "officielle" sur IDataRecord.
la source
En une seule ligne, utilisez ceci après votre récupération DataReader:
Ensuite,
Éditer
One-liner beaucoup plus efficace qui ne nécessite pas de charger le schéma:
la source
Voici un exemple de travail pour l'idée de Jasmin:
la source
cela fonctionne pour moi:
la source
Ce qui suit est simple et a fonctionné pour moi:
la source
Si vous avez lu la question, Michael a posé des questions sur DataReader, pas sur les gens de DataRecord. Obtenez vos objets à droite.
Utilisant un
r.GetSchemaTable().Columns.Contains(field)
sur un DataRecord fonctionne, mais il renvoie des colonnes BS (voir capture d'écran ci-dessous.)Pour voir si une colonne de données existe ET contient des données dans un DataReader, utilisez les extensions suivantes:
Usage:
L'appel
r.GetSchemaTable().Columns
sur un DataReader renvoie des colonnes BS:la source
IDataReader
met en œuvreIDataRecord
. Ce sont des interfaces différentes du même objet - tout commeICollection<T>
etIEnumerable<T>
sont des interfaces différentes deList<T>
.IDataReader
permet de passer à l'enregistrement suivant, tout enIDataRecord
permettant la lecture de l'enregistrement en cours. Les méthodes utilisées dans cette réponse proviennent toutes de l'IDataRecord
interface. Voir stackoverflow.com/a/1357743/221708 pour une explication de la raison pour laquelle déclarer le paramètre commeIDataRecord
préférable.r.GetSchemaTable().Columns
est une réponse absolument fausse à cette question.J'ai écrit pour les utilisateurs de Visual Basic:
Je pense que c'est plus puissant et l'utilisation est:
la source
Voici une version linq à une ligne de la réponse acceptée:
la source
Voici la solution de Jasmine en une seule ligne ... (une de plus, si simple!):
la source
la source
TLDR:
Beaucoup de réponses avec des affirmations sur les performances et les mauvaises pratiques, donc je clarifie cela ici.
La route d'exception est plus rapide pour un nombre plus élevé de colonnes retournées, la route de boucle est plus rapide pour un nombre inférieur de colonnes et le point de croisement est d'environ 11 colonnes. Faites défiler vers le bas pour voir un graphique et un code de test.
Réponse complète:
Le code de certaines des meilleures réponses fonctionne, mais il y a un débat sous-jacent ici pour la "meilleure" réponse basée sur l'acceptation de la gestion des exceptions dans la logique et ses performances associées.
Pour clarifier cela, je ne pense pas qu'il y ait beaucoup d'indications concernant les exceptions de capture. Microsoft a quelques conseils concernant les exceptions de LANCEMENT. Là, ils déclarent:
La première note est la clémence de «si possible». Plus important encore, la description donne ce contexte:
Cela signifie que si vous écrivez une API qui pourrait être utilisée par quelqu'un d'autre, donnez-lui la possibilité de naviguer dans une exception sans essayer / attraper. Par exemple, fournissez un TryParse avec votre méthode d'analyse d'exception. Cela ne dit nulle part que vous ne devriez pas attraper une exception.
De plus, comme le souligne un autre utilisateur, les captures ont toujours autorisé le filtrage par type et ont récemment permis un filtrage supplémentaire via la clause when . Cela semble être un gaspillage de fonctionnalités linguistiques si nous ne sommes pas censés les utiliser.
On peut dire qu'il y a QUELQUE coût pour une exception levée, et que le coût PEUT avoir un impact sur les performances dans une boucle lourde. Cependant, on peut également dire que le coût d'une exception va être négligeable dans une "application connectée". Le coût réel a été étudié il y a plus d'une décennie: https://stackoverflow.com/a/891230/852208 En d'autres termes, le coût d'une connexion et d'une requête d'une base de données est susceptible de éclipser celui d'une exception levée.
Tout cela mis à part, je voulais déterminer quelle méthode est vraiment la plus rapide. Comme prévu, il n'y a pas de réponse concrète.
Tout code qui boucle sur les colonnes devient plus lent à mesure que le nombre de colonnes existe. On peut également dire que tout code qui repose sur des exceptions ralentira en fonction de la vitesse à laquelle la requête ne sera pas trouvée.
En prenant les réponses de Chad Grant et de Matt Hamilton, j'ai exécuté les deux méthodes avec jusqu'à 20 colonnes et jusqu'à un taux d'erreur de 50% (l'OP a indiqué qu'il utilisait ces deux tests entre des procs différents, donc j'ai supposé aussi peu que deux) .
Voici les résultats, tracés avec LinqPad:
Les zigzags ici sont des taux d'erreur (colonne non trouvée) dans chaque nombre de colonnes.
Sur des ensembles de résultats plus étroits, le bouclage est un bon choix. Cependant, la méthode GetOrdinal / Exception n'est pas aussi sensible au nombre de colonnes et commence à surpasser la méthode de bouclage autour de 11 colonnes.
Cela dit, je n'ai pas vraiment de préférence en termes de performances, car 11 colonnes semblent raisonnables, car le nombre moyen de colonnes renvoyées sur une application entière. Dans les deux cas, nous parlons ici de fractions de milliseconde.
Cependant, du point de vue de la simplicité du code et de la prise en charge des alias, j'irais probablement avec la route GetOrdinal.
Voici le test sous forme linqpad. N'hésitez pas à republier avec votre propre méthode:
la source
Ce code corrige les problèmes que Levitikon avait avec leur code: (adapté de: [1]: http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable.aspx )
La raison pour laquelle vous obtenez tous ces noms de colonne inutiles et non le nom de la colonne de votre table ... est parce que vous obtenez le nom de la colonne de schéma (c'est-à-dire les noms de colonne de la table de schéma)
REMARQUE: cela semble renvoyer uniquement le nom de la première colonne ...
EDIT: code corrigé qui renvoie le nom de toutes les colonnes, mais vous ne pouvez pas utiliser un SqlDataReader pour le faire
la source
return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList();
:)Pour garder votre code robuste et propre, utilisez une seule fonction d'extension, comme ceci:
la source
Je n'ai pas non plus pu
GetSchemaTable
travailler, jusqu'à ce que je trouve cette voie .Fondamentalement, je fais ceci:
la source
Columns.Contains
est insensible à la casse.la source
Dans votre situation particulière (toutes les procédures ont les mêmes colonnes sauf 1 qui a 1 colonne supplémentaire), il sera meilleur et plus rapide de vérifier le lecteur. Propriété FieldCount pour les distinguer.
Je sais que c'est un ancien poste mais j'ai décidé de répondre pour aider les autres dans la même situation. vous pouvez également (pour des raisons de performances) mélanger cette solution avec la solution itérative de la solution.
la source
Ma classe d'accès aux données doit être rétrocompatible, donc j'essaie peut-être d'accéder à une colonne dans une version où elle n'existe pas encore dans la base de données. Nous avons renvoyé des ensembles de données assez volumineux, donc je ne suis pas un grand fan d'une méthode d'extension qui doit itérer la collection de colonnes DataReader pour chaque propriété.
J'ai une classe utilitaire qui crée une liste privée de colonnes, puis possède une méthode générique qui tente de résoudre une valeur basée sur un nom de colonne et un type de paramètre de sortie.
Ensuite, je peux simplement appeler mon code comme ça
la source
La clé de tout le problème est ici :
Si les trois lignes référencées (actuellement les lignes 72, 73 et 74) sont supprimées, vous pouvez facilement vérifier
-1
afin de déterminer si la colonne n'existe pas.La seule façon de contourner ce problème tout en garantissant des performances natives est d'utiliser une
Reflection
implémentation basée, comme suit:Utilisations:
La méthode d'extension basée sur la réflexion:
la source
Vous pouvez également appeler GetSchemaTable () sur votre DataReader si vous voulez la liste des colonnes et que vous ne voulez pas avoir à obtenir d'exception ...
la source
Que diriez-vous
Ce ne serait probablement pas aussi efficace dans une boucle
la source
dr.GetSchemaTable().Columns
contient - ce n'est pas ce que vous recherchez.Bien qu'il n'y ait pas de méthode exposée publiquement, une méthode existe dans la classe interne
System.Data.ProviderBase.FieldNameLookup
quiSqlDataReader
s'appuie.Pour y accéder et obtenir des performances natives, vous devez utiliser ILGenerator pour créer une méthode au moment de l'exécution. Le code suivant vous donnera un accès direct à
int IndexOf(string fieldName)
laSystem.Data.ProviderBase.FieldNameLookup
classe ainsi que la tenue de la comptabilité quiSqlDataReader.GetOrdinal()
fait qu'il n'y a aucun effet secondaire. Le code généré reflète l'existant,SqlDataReader.GetOrdinal()
sauf qu'il appelle à laFieldNameLookup.IndexOf()
place deFieldNameLookup.GetOrdinal()
. LaGetOrdinal()
méthode appelle laIndexOf()
fonction et lève une exception si-1
est renvoyée, nous contournons donc ce comportement.la source
ce travail pour moi
la source
vous pouvez obtenir plus de détails à partir d'ici: Pouvez-vous obtenir les noms de colonne à partir d'un SqlDataReader?
la source