42 votes

Quelle est la meilleure façon de traiter les DBNull's ?

J'ai souvent des problèmes avec DataRows renvoyé par SqlDataAdapters . Lorsque j'essaie de remplir un objet en utilisant un code comme celui-ci :

DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;

Quelle est la meilleure façon d'aborder DBNull's dans ce type de situation.

37voto

Dan Herbert Points 38336

Les types nullables sont une bonne chose, mais seulement pour les types qui ne sont pas nullables au départ.

Pour rendre un type "nullable", ajoutez un point d'interrogation au type, par exemple :

int? value = 5;

Je recommande également d'utiliser la fonction " as Le mot-clé " as " est utilisé à la place du casting. Vous ne pouvez utiliser le mot-clé "as" que sur les types nullables, donc assurez-vous que vous effectuez le casting de choses qui sont déjà nullables (comme les chaînes de caractères) ou que vous utilisez des types nullables comme mentionné ci-dessus. Le raisonnement est le suivant

  1. Si un type est nullable, le " as Le mot-clé " retourne null si une valeur est DBNull .
  2. C'est un peu plus rapide que la coulée bien que seulement dans certains cas . Ce n'est pas une raison suffisante pour utiliser les services de la Commission. as mais, associée à la raison ci-dessus, elle est utile.

Je recommande de faire quelque chose comme ça

DataRow row = ds.Tables[0].Rows[0];
string value = row as string;

Dans le cas ci-dessus, si row revient en tant que DBNull entonces value deviendra null au lieu de lever une exception. Soyez conscient que si votre requête DB change les colonnes/types retournés, en utilisant as fera échouer votre code en silence et rendre les valeurs simples null Il est donc recommandé de mettre en place des tests pour valider vos requêtes par d'autres moyens afin de garantir l'intégrité des données au fur et à mesure de l'évolution de votre code.

21voto

Daniel Auger Points 8459

Si vous n'utilisez pas de type nullable, la meilleure chose à faire est de vérifier si la valeur de la colonne est DBNull. Si c'est le cas, définissez votre référence à ce que vous utilisez pour null/empty pour le type de données correspondant.

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

Comme Manu l'a dit, vous pouvez créer une classe de conversion avec une méthode de conversion surchargée par type afin de ne pas avoir à truffer votre code de blocs if/else.

J'insiste cependant sur le fait que les types nullables sont la meilleure solution si vous pouvez les utiliser. Le raisonnement est le suivant : avec les types non nuls, vous allez devoir recourir à des "chiffres magiques" pour représenter les nuls. Par exemple, si vous mappez une colonne vers une variable int, comment allez-vous représenter DBNull ? Souvent, vous ne pouvez pas utiliser 0 parce que 0 a une signification valide dans la plupart des programmes. Souvent, je vois des gens faire correspondre DBNull à int.MinValue, mais cela pourrait potentiellement être problématique aussi. Mon meilleur conseil est le suivant :

  • Pour les colonnes qui peuvent être nulles dans la base de données, utilisez des types nullables.
  • Pour les colonnes qui ne peuvent pas être nulles dans la base de données, utilisez des types réguliers.

Les types nullables ont été créés pour résoudre ce problème. Ceci étant dit, si vous utilisez une ancienne version du framework ou si vous travaillez pour quelqu'un qui ne connaît pas les types nullables, l'exemple de code fera l'affaire.

8voto

Meff Points 4347

J'ai toujours trouvé clair, concis et sans problème l'utilisation d'une version de la vérification If/Else, mais avec l'opérateur ternaire. Tout reste sur une seule ligne, y compris l'attribution d'une valeur par défaut si la colonne est nulle.

Donc, supposons une colonne Int32 nullable nommée "MaCol", où nous voulons renvoyer -99 si la colonne est nulle, mais renvoyer la valeur entière si la colonne n'est pas nulle :

return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);

C'est la même méthode que le gagnant If/Else ci-dessus - Mais j'ai trouvé que si vous lisez plusieurs colonnes dans un datareader, c'est un vrai bonus d'avoir toutes les lignes de lecture de colonnes l'une sous l'autre, alignées, car il est plus facile de repérer les erreurs :

Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]);
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]);
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]);

8voto

Keith Points 46288

Ajouter une référence à System.Data.DataSetExtensions qui ajoute la prise en charge de Linq pour l'interrogation des tableaux de données.

Ce serait quelque chose comme :

string value = (
    from row in ds.Tables[0].Rows
    select row.Field<string>(0) ).FirstOrDefault();

5voto

Steve Schoon Points 31

Si vous avez le contrôle de la requête qui renvoie les résultats, vous pouvez utiliser ISNULL() pour renvoyer des valeurs non nulles comme ceci :

SELECT 
  ISNULL(name,'') AS name
  ,ISNULL(age, 0) AS age
FROM 
  names

Si votre situation peut tolérer ces valeurs magiques pour remplacer NULL, cette approche peut résoudre le problème dans toute votre application sans encombrer votre code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X