2009-10-03 11 views
5

Attualmente sto utilizzando un lettore di dati SQL (in vb.net) per estrarre un oggetto articolo tramite un processo memorizzato da un database di SQL Server 2008. Parte di questo oggetto comprende le due proprietà illustrate di seguito:Come posso restituire un valore a un sqldatareader se il valore è nullo?

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

mio problema è che la veridicità e rilevanza possono restituire un valore nullo e ciò causa la funzione di cadere.

Penso di aver capito perché. Sto chiedendo un valore intero (getin32) e poiché null viene restituito fallisce.

Come faccio ad accettare il valore nullo dal database in modo che non cada?

risposta

16

È possibile verificare se una determinata posizione ordinale è nullo utilizzando .IsDBNull() e quindi fare qualcosa - ad es. impostare il valore a -1 o qualcosa del genere:

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

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

Come Mike Hofer sottolinea nella sua risposta, si potrebbe anche avvolgere tutta questa logica in un metodo di estensione:

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

e poi basta usare quella " metodo SafeGetInt32" invece:

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

Marc

+0

o prendi l'eccezione e gestiscilo – Mark

+1

Sì, puoi farlo anche tu - ma evitare un'eccezione è meglio che prenderne e gestirne uno (in generale) –

+1

@marc_s: concordato con il tuo commento sulla mia risposta. Cancellandolo Grazie per averlo chiarito. Il tuo commento è stato "Non riesco a farlo funzionare, poiché se la colonna db è NULL, la chiamata .GetInt32() fallirà con un'eccezione - non recupererai un valore NULL che puoi quindi inserire nella" ?? "operator ...." – Mahin

3

hai controllato, SqlDataReader.IsDBNul l metodo? Probabilmente qualcosa del tipo:

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

beh, string.Empty non andrà molto bene se hai a che fare con una proprietà int32 ...... –

1

Sai, mi occupo di tutto questo tempo in Oracle. Per pulire il codice su, ho scritto una serie di metodi di estensione per semplificare l'operazione:

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

Creare un sovraccarico separato per ogni tipo che si desidera tornare. Lavoro principalmente con string, int, date e decimal. Ricorda YAGNI (non hai bisogno di lavorare con ogni tipo supportato dal lettore, solo quelli che effettivamente usi.)

Una classe di estensione come questa per SQL Server è davvero facile da scrivere e semplifica il tuo lavoro VASTAMENTE. Fidati di me. Ti mentirei? :)

+1

Non penso che funzionerà, dato che se la colonna nel database è NULL, quindi la chiamata a GetInt32() causerà un'eccezione. Non è possibile confrontare quella chiamata GetInt32() con DBNull.Value e reagire in tal senso ... –

+0

In realtà, funziona. Lo uso tutti i giorni. –

+0

La ragione per cui funziona è perché stiamo usando l'operatore di cortocircuito?:. Se non è nullo, lo restituiamo. Altrimenti, restituiremo il valore predefinito. –

0

Ecco ciò che usiamo su SQLServer e funziona come un fascino:

... 

    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) è di solito molto più lento che l'utilizzo di metodi come GetSqlInt32 e poi confrontando a DBNull.Value o usando il suo proprio .IsNull Like:

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

Provato alcune soluzioni modello, ma fino ad ora inutilmente. Il problema è che tutti i tipi di Sql-type (qui SqlInt32) sono in realtà delle strutture e mentre tutti hanno la proprietà .Value C# non ha template reali per gestirli. Inoltre hanno la loro interfaccia personalizzabile che ha solo. IsNull e non è compatibile con Nyllable <>.

Sospetto che uno abbia bisogno di un set completo di tipi Sql come modelli C# o di aggiungere ICOnvertible a questi per poter avere solo uno o due metodi basati su modelli.

Se qualcuno ha forse un'idea con un trucco funzionale o due parlare :-)

1

Questa versione generica può essere 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; 
    } 

potrebbe essere esteso a catturare InvalidCastException, o utilizzare Convert .ChangeType anziché casting?

+0

Mi piace questa opzione tranne per l'istruzione catch IndexOutOfRangeException. Penserei che vorresti sapere il più rapidamente possibile se la struttura dello schema è cambiata invece di spingere i valori predefiniti ad altre parti del tuo sistema. –

Problemi correlati