2010-03-12 16 views
35

Existe-t-il un moyen meilleur/plus propre de le faire?Poignée DBNull en C#

int stockvalue = 0; 
if (!Convert.IsDBNull(reader["StockValue"])) 
    stockvalue = (int)reader["StockValue"]; 
+0

Vous devriez également envisager des méthodes d'extension. Voici quelques exemples d'autres moyens possibles: http://shahanayyub.wordpress.com/2012/10/04/best-practice-to-check-for-dbnull-using-net/ – NeverHopeless

Répondre

6

Oui, vous pouvez utiliser int? cette façon, vous pouvez avoir une valeur par défaut de null au lieu de 0. Comme le résultat de stockvalue pourrait potentiellement être 0 il n'y a pas de confusion quant à savoir si la base de données était 0 ou nul. Par exemple, comme ceci (nullable), nous avons eu une initialisation par défaut de -1 pour représenter qu'aucune valeur n'a été affectée. Personnellement, je pensais que c'était un peu dangereux parce que si vous oubliez de le mettre à -1, il y a un problème de corruption de données qui peut être vraiment difficile à traquer.

http://msdn.microsoft.com/en-us/library/2cf62fcy(VS.80).aspx

int? stockvalue = null; 

if (!Convert.IsDBNull(reader["StockValue"])) 
    stockvalue = (int)reader["StockValue"]; 

//Then you can check 

if(stockValue.HasValue) 
{ 
    // do something here. 
} 
+2

+1 pour le type de données correct à utiliser. Cependant, IIRC, 'DBNull' doit encore être converti en' null' avec un peu de vérification. –

+1

utiliser int? Comment? Le simple fait de lancer le lecteur ["StockValue"] à (int?) Lancera une exception lorsque le lecteur ["StockValue"] est DBNull –

+0

vous pouvez utiliser int? de sorte que vous ne devez pas utiliser une initialisation par défaut à 0. – kemiller2002

0

utiliser le type Nullable<int> ... int? pour faire court

1

est ici une façon.

int stockvalue = Convert.IsDbNull(reader["StockValue"]) ? 0 : (int)reader["StockValue"]; 

Vous pouvez également utiliser TryParse

int stockvalue = 0 
Int32.TryParse(reader["StockValue"].ToString(), out stockvalue); 

Faites-nous savoir de quelle manière fonctionne pour vous

+0

Je ne me dérange pas les votes vers le bas, juste s'il vous plaît laissez un commentaire disant comment je pourrais améliorer cette réponse. –

+1

Il n'y a absolument aucune raison pour un DV ici car votre réponse est une solution correcte. –

+0

Oui, rien de mal à cela. Je vous ai voté pour l'équilibrer. :-) –

1

Vous pouvez faire cette conversion directement dans votre DB-requête, évitant ainsi alltogether le cas particulier. Mais je n'appellerais pas cela 'nettoyeur', à moins que vous ne puissiez utiliser ce formulaire de manière cohérente dans votre code, puisque vous perdriez des informations en retournant '0' au lieu de NULL dans la base de données.

+0

L'argument de la perte d'information a du sens. Dans une application de base de données, la valeur de "0" est différente de NULL. L'un dénote n'avoir aucune valeur, et l'autre est une valeur qui est simplement nulle. –

0

Pas vraiment. Vous pouvez encapsuler dans une méthode:

public int getDBIntValue(object value, int defaultValue) { 
    if (!Convert.IsDBNull(value)) { 
    return (int)value; 
    } 
    else { 
    return defaultValue; 
    } 

Et l'appeler comme ceci:

stockVaue = getDBIntVaue(reader["StockValue"], 0); 

Ou vous pouvez utiliser coalesce dans votre requête pour forcer la valeur retournée à être non nulle. Editer - corriger les erreurs de code muet sur la base des commentaires reçus.

+0

Et les downvotes sont parce que ...? – Ray

+3

@Ray: regardez votre code, et vous vous perdrez. –

+1

(Je ne vous ai pas downvote, mais) Pour un, vous avez passé dans reader ["StockValue"] dans le paramètre "value", mais ensuite ignoré le paramètre "value" dans la fonction et utilisé "reader [" StockValue "] "à la place, il ne sera pas compilé. –

10
int? stockvalue = (int?)(!Convert.IsDBNull(result) ? result : null); 

Une solution possible pour vous assurer que le DBNull traverse votre code. Pour notre groupe, en tant que meilleure pratique, nous essayons de ne pas autoriser les colonnes NULL dans la base de données à moins que ce ne soit vraiment nécessaire. Il y a plus d'overhead dans le codage pour le gérer, et parfois juste repenser le problème le rend si inutile.

+0

Je pense que cette solution est la plus claire et la plus flexible. Je suis allé avec quelque chose de presque identique mais ne permettant pas les valeurs nulles: Int32 aNum = Convert.IsDBNull (row ["stockvalue"])? 0: Convert.ToInt32 (row ["stockvalue"]) – Frug

28

La façon dont je le gérer est

int? stockvalue = reader["StockValue"] as int?; 

Très simple, propre et une ligne. Si, pour une raison quelconque, je ne peux absolument pas avoir une valeur nulle (pour laquelle je trouve un mauvais raisonnement, je préférerais savoir si une valeur a un sens ou si elle était unitalisée pour un type primitif):

int stockvalue = (reader["StockValue"] as int?).GetValueOrDefault(-1); 
+0

Ahh, j'ai une exception en utilisant '(int?) Reader [" ColumnName "]' et j'ai commencé à chercher la bonne façon de le faire. Cela devrait être accepté comme réponse. – Anlo

+0

Excellent. Cela préserve également l'état DBNull au cas où l'on serait intéressé à le savoir. D'autres solutions ici écartent ceci. – Jonas

6

Bien qu'il soit pratique de faire référence à reader["StockValue"], ce n'est pas très efficace. Il n'est pas non plus fortement typé, car il renvoie le type object.

Au lieu de cela, dans votre code, faire quelque chose comme ceci:

int stockValueOrdinal = reader.GetOrdinal("StockValue"); 
int? stockValue = reader.IsDbNull(stockValueOrdinal) ? 
    null : 
    reader.GetInt32(stockValueOrdinal); 

Bien sûr, il est préférable d'obtenir à un moment donné tous les ordinaux, puis les utiliser à travers le code.

+0

@John: dans votre commentaire sur ma réponse, vous m'avez demandé "et si la colonne changeait en double?". Le code que vous avez fourni ici lèvera une exception si la colonne contient une valeur qui n'est pas de type int. (aussi, détail mineur: il n'y a pas de méthode .GetInt() sur un lecteur de données Il devrait être .GetInt32()) –

+2

@Philippe: lancer une exception est _precisely_ ce que le code devrait faire si le type de colonne change et que le code ne l'est pas . Retourner à zéro quand quelque chose de grave est faux est une très mauvaise idée. –

+0

@Philippe: merci d'avoir attrapé la faute de frappe. –

55

Le plus court (à mon humble avis) est:

int stockvalue = (reader["StockValue"] as int?) ?? 0; 

Explication:

  • Si le lecteur [ "StockValue"] est de type int, la valeur sera retournée, et "??" opérateur renvoie le résultat
  • Si lecteur [ "StockValue"] est pas de type int (par exemple DBNull), null sera retourné, et le "??" l'opérateur renverra la valeur 0 (zéro).
+2

@Philippe: que se passe-t-il si le type de la colonne est changé en Double ou quelque chose? –

+1

+1 - C'est ainsi que j'écrirais le code, bien que si une valeur nulle est valide, la valeur par défaut n'est pas nécessaire; 'int? stockvalue = lecteur ["StockValue"] comme int? ' – stevehipwell

+2

@John: il retournera 0. Mais peu importe la solution que vous choisissez, si une colonne change de type de données, vous êtes foutu, sauf si vous lancez un appel à Convertir. ToInt32() ou quelque chose. –

0

J'ai deux méthodes d'extension suivantes dans mon projet:

public static T GetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor) 
     where T : class 
    { 
     T value; 
     if (dataReader.TryGetValueSafe(columnName, valueExtractor, out value)) 
     { 
      return value; 
     } 

     return null; 
    } 

    public static bool TryGetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor, out T value) 
    { 
     int ordinal = dataReader.GetOrdinal(columnName); 

     if (!dataReader.IsDBNull(ordinal)) 
     { 
      // Get value. 
      value = valueExtractor.Invoke(dataReader, ordinal); 

      return true; 
     } 

     value = default(T); 
     return false; 
    } 

L'utilisation peut ressembler à ceci:

string companyName = dataReader.GetValueSafe("CompanyName", (reader, ordinal) => reader.GetString(ordinal)); 
+1

Intéressant mais très parlant, j'ai créé une classe similaire à celle-ci quand je travaillais beaucoup avec DataReaders mais je l'ai rendu beaucoup plus concis comme 'reader.Get (" ReaderField ")' et travaillé avec la méthode 'ChangeType' –

+0

idée +1, mais je pense que ma mise en œuvre serait un peu plus rapide. –

10

j'ai écrit une méthode d'extension il y a plusieurs jours. En l'utilisant, vous pouvez simplement faire:

int? stockvalue = reader.GetValue<int?>("StockValue"); 

est ici la méthode d'extension (modifier pour répondre à vos besoins):

public static class ReaderHelper 
{ 
    public static bool IsNullableType(Type valueType) 
    { 
     return (valueType.IsGenericType && 
      valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))); 
    } 

    public static T GetValue<T>(this IDataReader reader, string columnName) 
    { 
     object value = reader[columnName]; 
     Type valueType = typeof(T); 
     if (value != DBNull.Value) 
     { 
      if (!IsNullableType(valueType)) 
      { 
       return (T)Convert.ChangeType(value, valueType); 
      } 
      else 
      { 
       NullableConverter nc = new NullableConverter(valueType); 
       return (T)Convert.ChangeType(value, nc.UnderlyingType); 
      } 
     } 
     return default(T); 
    } 
} 
+2

J'adore C#, et j'ai un code similaire à celui-ci dans un endroit ou 2, mais il est absolument retardé, nous aurions jamais besoin d'écrire du code comme ça. J'espère qu'avec des progrès continus dans le DLR, il y aura un jour où nous n'aurons jamais besoin d'écrire du code comme celui-ci pour faire du tapage. –

0
int? stockValue = reader["StockValue"] == null || reader["StockValue"] == DBNull.Value ? null : (int?)reader["StockValue"]; 
3
int stockvalue = reader["StockValue"] != DbNull.Value ? Convert.ToInt32(reader["StockValue"]) : 0;