2009-11-20 4 views
227

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 null, une exception est levée.SQL Data Reader - gestion des valeurs de colonne null

employee.FirstName = sqlreader.GetString(indexFirstName); 

Quelle est la meilleure façon de gérer les valeurs nulles dans cette situation?

Répondre

340

Vous devez vérifier IsDBNull:

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

C'est votre seul moyen fiable pour détecter et gérer cette situation.

J'enveloppées ces choses dans les méthodes d'extension et ont tendance à retourner une valeur par défaut si la colonne est en effet null:

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

comme cela, vous pouvez maintenant appeler:

employee.FirstName = SqlReader.SafeGetString(indexFirstName); 

et vous N'ayez plus jamais à vous soucier d'une exception ou d'une valeur null.

+39

Si quelqu'un a besoin du ** nom de colonne ** plutôt que de l'index, vous pouvez faire: int colIndex = reader.GetOrdinal (fieldname); 'et ea sily surcharge la fonction 'SafeGetString' de marc_s. – ilans

10

Une façon de le faire est de vérifier db nulls:

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

Nous utilisons une série de méthodes statiques pour tirer toutes les valeurs de nos lecteurs de données. Donc, dans ce cas, nous appellerions DBUtils.GetString(sqlreader(indexFirstName)) L'avantage de créer des méthodes statiques/partagées est que vous n'avez pas à faire les mêmes vérifications encore et encore ...

Les méthodes statiques devraient contenir code pour vérifier les valeurs nulles (voir les autres réponses sur cette page).

6

Ce que j'ai tendance à faire est de remplacer les valeurs nulles dans l'instruction SELECT par quelque chose de approprié.

SELECT ISNULL(firstname, '') FROM people 

Ici, je remplace chaque null avec une chaîne vide. Votre code ne jettera pas d'erreur dans ce cas.

+0

Si possible, utilisez cette option pour éviter les valeurs nulles. Sinon, j'aime la réponse de Sonny Boy aux méthodes d'assistance. –

+3

Pourquoi une méthode d'aide statique et séparée? Une méthode d'extension sur le SqlDataReader ne semble-t-elle pas plus convaincante et plus intuitive? –

0

et/ou utiliser l'opérateur ternaire avec affectation:

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
        String.Empty: rdr.GetString(indexFirstName); 

remplacer la valeur par défaut (lorsque null) valeur appropriée pour chaque type de propriété ...

4

Vérifiez sqlreader.IsDBNull(indexFirstName) avant d'essayer de le lire.

2

Je pense que vous voulez utiliser:

SqlReader.IsDBNull(indexFirstName) 
13

IsDbNull(int) est généralement beaucoup plus lent que d'utiliser des méthodes telles que GetSqlDateTime puis à comparer 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]); 
+4

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

189

Vous devez utiliser l'opérateur as combiné avec l'opérateur ?? pour les valeurs par défaut. Les types de valeur devront être lus comme valables et donnés par défaut.

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

L'opérateur as gère la coulée, y compris la vérification de DBNull.

+5

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

+0

Je me demande si vous pouvez remplacer la valeur par défaut (int) à -1 au lieu de 0 – Chris

+4

@Chris - Vous devriez pouvoir remplacer par défaut (int) avec -1. – stevehipwell

-2

vous pouvez toujours vérifier cela aussi bien

if(null !=x && x.HasRows) 
{ ....} 
+0

-1 Ce n'est pas le point: nous traitons le cas d'une valeur de colonne null, pas celle d'un SqlDataReader nul ou vide – bluish

9

Je ne pense pas qu'il y ait une valeur de colonne NULL, lorsque les lignes sont renvoyées dans un datareader en utilisant le nom de la colonne. Si vous 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'utiliser ce qui suit et ne vous inquiétez pas trop:

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

Vous pouvez faire un lecteur [FieldName] == DBNull.Value, pour vérifier –

7

Cette solution est moins dépendante du fournisseur et travaille avec un SQL, OleDB et MySQL Lecteur:

public static string GetStringSafe(this IDataReader reader, int colIndex) 
{ 
    return GetStringSafe(reader, colIndex, string.Empty); 
} 

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue) 
{ 
    if (!reader.IsDBNull(colIndex)) 
     return reader.GetString(colIndex); 
    else 
     return defaultValue; 
} 

public static string GetStringSafe(this IDataReader reader, string indexName) 
{ 
    return GetStringSafe(reader, reader.GetOrdinal(indexName)); 
} 

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue) 
{ 
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue); 
} 
+1

de NULL Copier et personnaliser ce code directement dans une classe d'extensions en ce moment. – condiosluzverde

0

J'utilise le code répertorié ci-dessous pour gérer des cellules null dans une feuille Excel qui est lue dans un datatable.

if (!reader.IsDBNull(2)) 
{ 
    row["Oracle"] = (string)reader[2]; 
} 
0
private static void Render(IList<ListData> list, IDataReader reader) 
     { 
      while (reader.Read()) 
      { 

       listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null; 
       //没有这一列时,让其等于null 
       list.Add(listData); 
      } 
      reader.Close(); 
     } 
22

Pour une chaîne, vous pouvez simplement lancer la version d'objet (accessible à l'aide de l'opérateur de tableau) et le vent avec une chaîne vide pour nulls:

employee.FirstName = (string)sqlreader[indexFirstName]; 

ou

employee.FirstName = sqlreader[indexFirstName] as string; 

Pour les entiers, si vous transtypez en un entier nul, vous pouvez utiliser GetValueOrDefault()

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault(); 

ou l'opérateur à coalescence nulle (??).

employee.Age = (sqlreader[indexAge] as int?) ?? 0; 
+2

Je préfère cette approche à la réponse acceptée. – liang

+1

convenu que c'est beaucoup plus simple et cela fonctionne, – visual

+1

Distribution explicite, comme dans votre premier exemple, ne fonctionne pas. Il renvoie la même erreur – musefan

0

Cette méthode dépend de indexFirstName qui devrait être l'ordinal de colonne de base zéro.

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

Si vous ne connaissez pas l'index de colonne, mais wan't pour vérifier un nom que vous pouvez utiliser cette méthode d'extension à la place:

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; 
    } 
} 

Et utiliser la méthode comme ceci:

if(sqlReader.HasColumn("FirstName")) 
{ 
    employee.FirstName = sqlreader["FirstName"]; 
} 
8

reader.IsDbNull(ColumnIndex) fonctionne autant de réponses dit.

Et je tiens à mentionner que si vous travaillez avec des noms de colonnes, la simple comparaison des types peut être plus confortable.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic } 
+0

Cela fonctionne également sur les anciennes versions de System.Data et .NET FW – RaSor

2

Vous pouvez écrire une fonction générique pour vérifier Null et inclure la valeur par défaut lorsqu'elle est NULL. Appelez ce lors de la lecture Datareader

public T CheckNull<T>(object obj) 
     { 
      return (obj == DBNull.Value ? default(T) : (T)obj); 
     } 

Lors de la lecture de la Datareader utiliser

     while (dr.Read()) 
         { 
          tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon(); 
          Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]); 
          Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]); 
          Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]); 
         } 
0

Vous pouvez utiliser l'opérateur conditionnel:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : ""; 
+0

Identique à l'une des réponses ci-dessous, mais 8 ans de retard! – beercohol

1

comment sur la création des méthodes d'aide

For String

private static string MyStringConverter(object o) 
    { 
     if (o == DBNull.Value || o == null) 
      return ""; 

     return o.ToString(); 
    } 

Utilisation

MyStringConverter(read["indexStringValue"]) 

Pour Int

private static int MyIntonverter(object o) 
    { 
     if (o == DBNull.Value || o == null) 
      return 0; 

     return Convert.ToInt32(o); 
    } 

Utilisation

MyIntonverter(read["indexIntValue"]) 

Pour Date de

private static DateTime? MyDateConverter(object o) 
    { 
     return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o); 
    } 

Utilisation

MyDateConverter(read["indexDateValue"]) 

Note: pour DateTime déclarer varialbe comme

DateTime? variable; 
1

question ancienne, mais peut-être quelqu'un encore besoin d'une réponse

en temps réel j'ai travaillé autour de cette question comme ça

Pour int:

public static object GatDataInt(string Query, string Column) 
    { 
     SqlConnection DBConn = new SqlConnection(ConnectionString); 
     if (DBConn.State == ConnectionState.Closed) 
      DBConn.Open(); 
     SqlCommand CMD = new SqlCommand(Query, DBConn); 
     SqlDataReader RDR = CMD.ExecuteReader(); 
     if (RDR.Read()) 
     { 
      var Result = RDR[Column]; 
      RDR.Close(); 
      DBConn.Close(); 
      return Result; 
     } 
     return 0; 
    } 

la même chose pour la chaîne juste retour « » au lieu de 0 comme « » est une chaîne vide

afin que vous puissiez l'utiliser comme

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?; 

et

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string; 

très flexible afin que vous puissiez insérer n'importe quelle requête pour lire n'importe quelle colonne et elle ne retournera jamais avec l'erreur

Questions connexes