340 votes

Lecteur de données SQL - traitement des valeurs de colonnes nulles

J'utilise un SQLdatareader pour créer des POCO à partir d'une base de données. Le code fonctionne sauf lorsqu'il rencontre une valeur nulle dans la base de données. Par exemple, si la colonne FirstName de la base de données contient une valeur nulle, une exception est levée.

employee.FirstName = sqlreader.GetString(indexFirstName);

Quelle est la meilleure façon de traiter les valeurs nulles dans cette situation ?

1 votes

Je pense que la fonction générique créée par @Vijai devrait être davantage reconnue dans ce fil. Elle a très bien fonctionné pour moi et je n'ai pas besoin de passer l'objet lecteur de données à chaque fois. Beaucoup de réponses populaires passent l'objet lecteur de données qui est inutile et lourd.

539voto

marc_s Points 321990

Vous devez vérifier IsDBNull :

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

C'est le seul moyen fiable de détecter et de gérer cette situation.

J'ai enveloppé ces choses dans des méthodes d'extension et tendent à retourner une valeur par défaut si la colonne est effectivement null :

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

Maintenant, vous pouvez l'appeler comme ça :

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

et vous n'aurez jamais à vous soucier d'une exception ou d'une null valeur à nouveau.

77 votes

Si quelqu'un a besoin du nom de la colonne plutôt que l'index, vous pouvez le faire : int colIndex = reader.GetOrdinal(fieldname); et de surcharger facilement l'outil de @marc_s SafeGetString fonction.

6 votes

Je ne peux pas croire que je suis ici en 2019, en VB non moins.......... Merci quand même, c'est d'une grande aide

0 votes

On peut aussi faire comme ceci : int ordinal = reader.GetOrdinal("col_name") ; uint ? val = reader.IsDBNull(ordinal) ? (uint ?)null : reader.GetUInt32(ordinal) ;

237voto

Stevo3000 Points 12725

Vous devez utiliser le as combiné avec l'opérateur ?? pour les valeurs par défaut. Les types de valeurs devront être lus comme nullables et se voir attribuer une valeur par défaut.

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

Le site as L'opérateur gère le casting, y compris la vérification de DBNull.

6 votes

Si quelqu'un change la colonne Age d'un int à un bigint SQL (c# long), votre code échouera silencieusement en retournant 0. La réponse de ZXX est plus fiable IMO.

0 votes

Je me demande si vous pouvez modifier la valeur par défaut (int) pour qu'elle soit de -1 au lieu de 0.

6 votes

@Chris - Vous devriez pouvoir remplacer default(int) par -1.

27voto

ZXX Points 3216

IsDbNull(int) est généralement beaucoup plus lent que l'utilisation de méthodes comme GetSqlDateTime puis en le comparant à DBNull.Value . Essayez ces méthodes d'extension pour SqlDataReader .

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

Utilisez-les comme ceci :

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

5 votes

Je trouve que les opérateurs explicites sur les types System.Data.SqlTypes provoquent des erreurs partout en essayant d'utiliser ce code...

0 votes

Voir stackoverflow.com/a/21024873/1508467 pour une explication de la raison pour laquelle cela échoue parfois (essayez d'utiliser Val<int> pour lire une colonne SQL int).

14voto

el bayames Points 79

Je ne pense pas qu'il y ait NULL la valeur de la colonne, lorsque des lignes sont renvoyées dans un datareader en utilisant le nom de la colonne.

Si vous le faites datareader["columnName"].ToString(); il vous donnera toujours une valeur qui peut être une chaîne vide ( String.Empty si vous avez besoin de comparer).

J'utiliserais les éléments suivants et ne m'inquiéterais pas trop :

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();

4 votes

Vous pouvez faire un reader[FieldName] == DBNull.Value, pour vérifier si les NULL sont présents.

13voto

Michael Todd Points 9384

Une façon de le faire est de vérifier la présence de nuls dans la base de données :

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));

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