Cette question revient occasionnellement, mais je n'ai pas vu de réponse satisfaisante.
Un modèle typique est (la ligne est un DataRow ):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
Ma première question est de savoir lequel est le plus efficace (j'ai inversé la condition):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
Cela indique que .GetType () devrait être plus rapide, mais peut-être que le compilateur connaît quelques astuces que je ne connais pas?
Deuxième question, vaut-il la peine de mettre en cache la valeur de la ligne ["valeur"] ou le compilateur optimise-t-il l'indexeur de toute façon?
Par exemple:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Remarques:
- la ligne ["valeur"] existe.
- Je ne connais pas l'index de colonne de la colonne (d'où la recherche de nom de colonne).
- Je pose des questions spécifiquement sur la vérification de DBNull puis sur l'affectation (pas sur l'optimisation prématurée, etc.).
J'ai comparé quelques scénarios (temps en secondes, 10 000 000 essais):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals a les mêmes performances que "=="
Le résultat le plus intéressant? Si vous ne correspondez pas au nom de la colonne par cas (par exemple, "Valeur" au lieu de "valeur", cela prend environ dix fois plus de temps (pour une chaîne):
row["Value"] == DBNull.Value: 00:00:12.2792374
La morale de l'histoire semble être que si vous ne pouvez pas rechercher une colonne par son index, assurez-vous que le nom de la colonne que vous fournissez à l'indexeur correspond exactement au nom de DataColumn.
La mise en cache de la valeur semble également être presque deux fois plus rapide:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
La méthode la plus efficace semble donc être:
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
IDataRecord
extensions.Réponses:
J'ai dû louper quelque chose. N'est-ce pas vérifier
DBNull
exactement ce que fait laDataRow.IsNull
méthode?J'ai utilisé les deux méthodes d'extension suivantes:
Usage:
Si vous ne voulez pas de
Nullable<T>
valeurs de retour pourGetValue<T>
, vous pouvez facilement retournerdefault(T)
ou une autre option à la place.Sur une note non liée, voici une alternative VB.NET à la suggestion de Stevo3000:
la source
row.IsNull(columnName)
vous le lisez déjà une fois et le relisez. Ne pas dire que cela fera une différence, mais théoriquement, cela peut être moins efficace.System.Data.DataSetExtensions.DataRowExtensions.Field<T>(this System.Data.DataRow, string)
ce que faire essentiellement la même chose que la première méthode?Vous devez utiliser la méthode:
Étant donné qu'il est intégré au Framework, je m'attendrais à ce que ce soit le plus efficace.
Je suggérerais quelque chose du genre:
Et oui, le compilateur doit le mettre en cache pour vous.
la source
Le compilateur n'optimisera pas l'indexeur (c'est-à-dire si vous utilisez la ligne ["value"] deux fois), donc oui, c'est un peu plus rapide à faire:
puis utilisez la valeur deux fois; l'utilisation de .GetType () risque de poser des problèmes s'il est nul ...
DBNull.Value
est en fait un singleton, donc pour ajouter une 4ème option - vous pourriez peut-être utiliser ReferenceEquals - mais en réalité, je pense que vous vous inquiétez trop ici ... Je ne pense pas que la vitesse diffère entre "is", "== "etc va être la cause de tout problème de performance que vous rencontrez. Profilez votre code entier et concentrez-vous sur quelque chose qui compte ... ce ne sera pas ça.la source
J'utiliserais le code suivant en C # ( VB.NET n'est pas aussi simple).
Le code affecte la valeur si elle n'est pas nulle / DBNull, sinon il attribue la valeur par défaut qui pourrait être définie sur la valeur LHS permettant au compilateur d'ignorer l'affectation.
la source
oSomeObject.IntMember = If(TryCast(oRow("Value), Integer?), iDefault)
.TryCast
cela ne fournit pas les mêmes fonctionnalités pratiques que l'as
opérateur C # pour lesNullable(Of T)
types. Le moyen le plus proche d'imiter cela est d'écrire votre propre fonction, comme je l'ai maintenant suggéré dans ma réponse.Je pense que très peu d'approches ici ne risquent pas le plus d'inquiéter la perspective OP (Marc Gravell, Stevo3000, Richard Szalay, Neil, Darren Koppand) et la plupart sont inutilement complexes. Étant pleinement conscient qu'il s'agit d'une micro-optimisation inutile, laissez-moi vous dire que vous devriez essentiellement les utiliser:
1) Ne lisez pas la valeur de DataReader / DataRow deux fois - alors mettez-la en cache avant les vérifications et les casts / conversions nulles ou mieux encore passez directement votre
record[X]
objet à une méthode d'extension personnalisée avec la signature appropriée.2) Pour obéir à ce qui précède, n'utilisez pas la
IsDBNull
fonction intégrée sur votre DataReader / DataRow car cela appelle le enrecord[X]
interne, donc en fait vous le ferez deux fois.3) La comparaison de type sera toujours plus lente que la comparaison de valeurs en règle générale. Fais
record[X] == DBNull.Value
mieux.4) Le lancer direct sera plus rapide que d'appeler la
Convert
classe pour la conversion, même si je crains que cette dernière ne faiblisse moins.5) Enfin, accéder à l'enregistrement par index plutôt que par nom de colonne sera à nouveau plus rapide.
Je pense que passer par les approches de Szalay, Neil et Darren Koppand sera mieux. J'aime particulièrement l'approche de la méthode d'extension de Darren Koppand qui prend en compte
IDataRecord
(bien que je voudrais le restreindre davantageIDataReader
) et le nom de l'index / de la colonne.Prenez soin de l'appeler:
et pas
au cas où vous auriez besoin de faire la différence entre
0
etDBNull
. Par exemple, si vous avez des valeurs nulles dans les champs d'énumération, sinon ladefault(MyEnum)
première valeur d'énumération risque d'être renvoyée. Alors mieux vaut appelerrecord.GetColumnValue<MyEnum?>("Field")
.Puisque vous lisez à partir d'un
DataRow
, je créerais une méthode d'extension pour les deuxDataRow
etIDataReader
en DRYing le code commun.Alors maintenant, appelez-le comme:
Je pense que c'est ainsi que cela aurait dû être dans le cadre (au lieu des méthodes
record.GetInt32
,record.GetString
etc.) en premier lieu - aucune exception d'exécution et nous donne la flexibilité de gérer les valeurs nulles.D'après mon expérience, j'ai eu moins de chance avec une méthode générique pour lire à partir de la base de données. J'ai toujours eu à traiter différents types de mesure, donc je devais écrire mes propres
GetInt
,GetEnum
,GetGuid
, etc. méthodes à long terme. Que faire si vous souhaitez réduire les espaces blancs lors de la lecture d'une chaîne à partir de db par défaut ou la traiterDBNull
comme une chaîne vide? Ou si votre décimal doit être tronqué de tous les zéros de fin. J'ai eu le plus de problèmes avec leGuid
type où différents pilotes de connecteur se comportaient différemment lorsque les bases de données sous-jacentes peuvent les stocker sous forme de chaîne ou de binaire. J'ai une surcharge comme celle-ci:Avec l'approche de Stevo3000, je trouve l'appel un peu laid et fastidieux, et il sera plus difficile d'en faire une fonction générique.
la source
Il y a le cas gênant où l'objet pourrait être une chaîne. Le code de méthode d'extension ci-dessous gère tous les cas. Voici comment vous l'utiliseriez:
la source
Je préfère personnellement cette syntaxe, qui utilise la méthode explicite IsDbNull exposée par
IDataRecord
, et met en cache l'index de colonne pour éviter une recherche de chaîne en double.Développé pour la lisibilité, cela ressemble à:
Réécrit pour tenir sur une seule ligne pour la compacité du code DAL - notez que dans cet exemple, nous attribuons
int bar = -1
ifrow["Bar"]
est nul.L'affectation en ligne peut être déroutante si vous ne savez pas qu'elle est là, mais elle conserve toute l'opération sur une seule ligne, ce qui, je pense, améliore la lisibilité lorsque vous remplissez les propriétés de plusieurs colonnes dans un bloc de code.
la source
Non pas que j'ai fait cela, mais vous pouvez contourner l'appel du double indexeur tout en gardant votre code propre en utilisant une méthode statique / d'extension.
C'est à dire.
Ensuite:
A également l'avantage de conserver la logique de vérification nulle au même endroit. L'inconvénient est, bien sûr, qu'il s'agit d'un appel de méthode supplémentaire.
Juste une pensée.
la source
J'essaie d'éviter ce contrôle autant que possible.
Évidemment, cela n'a pas besoin d'être fait pour les colonnes qui ne peuvent pas tenir
null
.Si vous stockez dans un type valeur Nullable (
int?
, etc.), vous pouvez simplement convertir en utilisantas int?
.Si vous n'avez pas besoin de faire la différence entre
string.Empty
etnull
, vous pouvez simplement appeler.ToString()
, car DBNull reviendrastring.Empty
.la source
J'utilise toujours:
Je l'ai trouvé court et complet.
la source
Voici comment je gère la lecture à partir de DataRows
Exemple d'utilisation:
Les accessoires de Monsters Got My .Net pour le code ChageTypeTo.
la source
J'ai fait quelque chose de similaire avec les méthodes d'extension. Voici mon code:
Pour l'utiliser, vous feriez quelque chose comme
la source
si dans un DataRow la ligne ["fieldname"] isDbNull remplacez-la par 0 sinon obtenez la valeur décimale:
la source
utiliser comme ça
la source
J'ai IsDBNull dans un programme qui lit beaucoup de données à partir d'une base de données. Avec IsDBNull, il charge les données en 20 secondes environ. Sans IsDBNull, environ 1 seconde.
Donc je pense qu'il vaut mieux utiliser:
la source