2009-10-03 8 views
5

J'utilise actuellement un lecteur de données SQL (dans vb.net) pour extraire un objet article via un proc stocké à partir d'une base de données SQL Server 2008. Une partie de cet objet comprend les deux propriétés ci-dessous:Comment renvoyer une valeur à un sqldatareader si la valeur est null?

theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness")))) 
theArticle.Relevance = ((myReader.GetInt32(myReader.GetOrdinal("Relevance")))) 

Mon problème est que la véracité et la pertinence peuvent renvoyer une valeur NULL et cela crée la fonction de tomber.

Je pense que je comprends pourquoi. Je demande une valeur entière (getin32) et parce que null est retourné il échoue.

Comment accepter la valeur nulle de la base de données afin qu'elle ne tombe pas?

Répondre

16

Vous pouvez vérifier si une position ordinale donnée est nulle en utilisant .IsDBNull() et ensuite faire quelque chose - par ex. définir votre valeur à -1 ou quelque chose:

int myOrdinal = myReader.GetOrdinal("Truthfullness"); 

if(myReader.IsDBNull(myOrdinal)) 
{ 
    theArticle.Truthfulness = -1; 
} 
else 
{ 
    theArticle.Truthfulness = myReader.GetInt32(myOrdinal); 
} 

Comme le souligne Mike Hofer dans sa réponse, vous pouvez aussi envelopper toute cette logique dans une méthode d'extension:

public static class SqlDataReaderExtensions 
{ 
    public static int SafeGetInt32(this SqlDataReader reader, 
            string columnName, int defaultValue) 
    { 
     int ordinal = reader.GetOrdinal(columnName); 

     if(!reader.IsDbNull(ordinal)) 
     { 
      return reader.GetInt32(ordinal); 
     } 
     else 
     { 
      return defaultValue; 
     } 
    } 
} 

puis il suffit d'utiliser ce " SafeGetInt32" méthode à la place:

theArticle.Truthfulness = myReader.SafeGetInt32("Truthfullness", -1); 

Marc

+0

ou attraper l'exception – Mark

+1

Oui, vous pouvez le faire aussi - mais il est préférable d'éviter une exception que d'en attraper et de gérer un (en général) –

+1

@marc_s: D'accord avec votre commentaire sur ma réponse. Supprimer le. Merci de le préciser. Votre commentaire était "Je ne pense pas que cela fonctionnera, car si la colonne db est NULL, l'appel .GetInt32() échouera avec une exception - vous ne récupérerez pas une valeur NULL que vous pourrez ensuite ajouter à la" ?? "opérateur ...." – Mahin

3

Avez-vous vérifié, SqlDataReader.IsDBNul l Méthode? Probablement quelque chose comme:

if(myReader.IsDBNull(myReader.GetOrdinal("Truthfulness")) 
theArticle.Truthfulness = string.Empty; 
else 
theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness")))) 
+0

bien, string.Empty ne fera pas beaucoup de bien si vous avez affaire à une propriété int32 ...... –

1

Vous savez, je traite avec tout le temps dans Oracle. Pour nettoyer la place de code, je l'ai écrit un ensemble de méthodes d'extension pour simplifier l'opération:

using System.Data.OracleClient; 
public static class OracleDataReaderExtensions 
{ 
    public static int GetInt32(this OracleDataReader reader, string columnName, int defaultValue) 
    { 
     return reader.GetInt32(reader.GetOrdinal(columnName)) != DbNull.Value ? 
       reader.GetInt32(reader.GetOrdinal(columnName)) : 
       defaultValue; 
    } 
} 

Créer une surcharge pour chaque type que vous souhaitez revenir. Je travaille principalement avec string, int, date et decimal. Souvenez-vous de YAGNI (vous n'avez pas besoin de travailler avec tous les types supportés par le lecteur, seulement ceux que vous utilisez réellement.)

Une classe d'extension comme celle-ci pour SQL Server est vraiment facile à écrire et simplifiera BEAUCOUP votre travail. Croyez-moi sur ce point. Est-ce que je vous mentirais? :)

+1

Je ne pense pas que cela fonctionnera, puisque si la colonne Dans la base de données est NULL, l'appel à GetInt32() provoquera une exception. Vous ne pouvez pas comparer cet appel GetInt32() contre DBNull.Value et réagir sur cela .... –

+0

En fait, cela fonctionne. Je l'utilise tous les jours. –

+0

La raison pour laquelle cela fonctionne est parce que nous utilisons l'opérateur de court-circuit?:. Si ce n'est pas nul, nous le renvoyons. Sinon, nous retournons la valeur par défaut. –

0

Voici ce que nous utilisons sur SQLServer et il fonctionne comme un charme:

... 

    Dim X as Object = pbDr("TotAmt") 'dr is dim'ed as a DataReader 

... 

    Public Function pbDr(ByVal drName As String) As Object 

    Dim SQLError As SqlClient.SqlException 

    Dim IsNull As Boolean 

    Dim Ordinal, DispNbr As Integer 

    Try 
     Ordinal = dr.GetOrdinal(drName) 
     IsNull = dr.IsDBNull(Ordinal) 
     If IsNull Then 
     Dim Dbtype As String = dr.GetFieldType(Ordinal).ToString 
     If Dbtype = "System.String" Then 
      Return "" 
     ElseIf Dbtype = "System.Int32" _ 
     OrElse Dbtype = "System.Double" _ 
     OrElse Dbtype = "System.Decimal" _ 
     OrElse Dbtype = "System.Int16" Then 
      Return 0 
     Else 
      MsgBox("Print This Screen And Send To Support" _ 
      & "pbdr-Object = " & Dbtype, MsgBoxStyle.Critical) 
      Return "" 
     End If 
     Else 
     Return dr(Ordinal) 
     End If 

    Catch sqlerror 
     Call DispSQLError(SQLError, "pbDr") 
     pbDr = "" 
    End Try 

    End Function 
0

IsDBNull (int) est généralement beaucoup plus lent que l'utilisation des méthodes comme GetSqlInt32 puis comparer à DBNull.Value ou de l'utiliser est propre .IsNull Comme:

public static int Int32(this SqlDataReader r, int ord) 
    { 
     var t = r.GetSqlInt32(ord); 
     return t.IsNull ? default(int) : t.Value; 
    } 

Essayé quelques solutions de modèle, mais en vain jusqu'à présent. Le problème est que tous les types Sql-types (SqlInt32 ici) sont en fait des structures et alors qu'ils ont tous la propriété .Value C# n'a pas de vrai modèle pour gérer cela. En outre, ils ont leur propre interface INullable qui a seulement .IsNull et n'est pas compatible avec Nyllable <>.

Je soupçonne que l'on aurait besoin d'un ensemble complet de types Sql en tant que modèles C# ou d'ajouter ICOnvertible à eux afin de pouvoir avoir seulement une ou deux méthodes basées sur des modèles.

Si quelqu'un a peut-être une idée avec une astuce fonctionnelle ou deux parler :-)

1

Cette version générique peut être utile:

private T ValueOrDefault<T>(System.Data.IDataReader rdr, string columnName) 
    { 
     T vod = default(T); 
     try 
     { 
      int idx = rdr.GetOrdinal(columnName); 
      if (!rdr.IsDBNull(idx)) 
       return (T)rdr[idx]; 
     } 
     catch (IndexOutOfRangeException) { } 

     return vod; 
    } 

pourrait être étendu pour attraper InvalidCastException, ou utilisez Convert .ChangeType au lieu de lancer?

+0

J'aime cette option à l'exception de l'instruction catch IndexOutOfRangeException. Je pense que vous voulez savoir aussi rapidement que possible si la structure du schéma a changé au lieu de pousser les valeurs par défaut à d'autres parties de votre système. –

Questions connexes