2012-02-24 12 views
17

Se devo codice simile al seguente:Possibile usare ?? (l'operatore di coalesce) con DBNull?

while(myDataReader.Read()) 
{ 
    myObject.intVal = Convert.ToInt32(myDataReader["mycolumn"] ?? 0); 
} 

getta l'errore:

Object cannot be cast from DBNull to other types.

definire intVal come int nullable non è un'opzione. C'è un modo per me di fare quanto sopra?

+3

possibile duplicato del [Handling DBNull in C#] (http: // StackOverflow .com/questions/2433155/handle-dbnull-in-c-sharp) – BoltClock

risposta

13

È possibile utilizzare un metodo di estensione? (Scritto fuori dalla parte superiore della mia testa)

public static class DataReaderExtensions 
{ 
    public static T Read<T>(this SqlDataReader reader, string column, T defaultValue = default(T)) 
    { 
     var value = reader[column]; 

     return (T)((DBNull.Value.Equals(value)) 
        ? defaultValue 
        : Convert.ChangeType(value, typeof(T))); 
    } 
} 

devi usare le cose come:

while(myDataReader.Read()) 
{ 
    int i = myDataReader.Read<int>("mycolumn", 0); 
} 
+0

Ho finito per seguire il percorso del metodo di estensione. Stavo ricevendo un errore con la parte 'Convert.ChangeType ...' quindi ho apportato alcune modifiche minori. –

+0

Puoi aggiornare la mia risposta con i "piccoli cambiamenti" che dovevi fare? –

+0

In realtà sto ancora avendo qualche problema a far funzionare il casting. 'Convertire.ChangeType (reader [column], typeof (T)) 'non viene compilato, quindi sono passato a questo' (T) reader [column] 'che genera un'eccezione di runtime. Qualche idea? –

2

perché non usare qualcosa di diverso dall'operatore nulla coalescenza (DBNull.Value = null!):

int i = myDataReader["mycolumn"] == DBNull.Value ? 
      Convert.ToInt32(myDataReader["mycolumn"]) : 
      0; 

Si può sempre avvolgetelo in un metodo di estensione ordinata:

public static T Read<T>(this DataReader reader, string column, T defaultVal) 
{ 
    if(reader[column] == DBNull.Value) return defaultVal; 
    return Convert.ChangeType(reader[column], typeof(T)); 
} 
+1

Ho considerato un ternario se ma "??" sembra più pulito se riesco a farcela. –

+0

@AbeMiessler Non penso che '??' funzionerà con 'DBNull'; in realtà non è "null". – dlev

+0

Hai ottenuto il contrario. Inoltre suggerirei che questo metodo non sia così efficiente. – nawfal

1

No, funziona solo per i valori null.

Come su un metodo di estensione su oggetto che controlla DBNull e restituisce invece un valore predefinito?

//may not compile or be syntactically correct! Just the general idea. 
public static object DefaultIfDBNull(this object TheObject, object DefaultValue) 
{ 
    if(TheObject is DBNull) 
     return DefaultValue; 
    return TheObject; 
} 
+0

Perché non utilizzare i generici piuttosto che l'oggetto? – dlev

+0

@dlev Nel contesto di trattare con il datareader non generico ho pensato che sarebbe stato più semplice. Era appena fuori dalla mia testa. La risposta generica di insta sembra piuttosto buona. – asawyer

5

ne dite qualcosa di simile:

object x = DBNull.Value; 
int y = (x as Int32?).GetValueOrDefault(); //This will be 0 

o nel vostro caso:

int i = (myDataReader["mycolumn"] as Int32?).GetValueOrDefault(); 
+0

Come ha sottolineato John Saunders nella sua risposta alla domanda "possibile duplicato" collegata, questo approccio ha lo svantaggio di non riuscire a generare un'eccezione se il tipo di dati della colonna cambia. Ad esempio, se "mycolumn" contiene un valore 'short' di 42, la variabile' i' avrà un valore di '0'. Un cast diretto genererebbe un'eccezione, richiamando l'attenzione sulla mancata corrispondenza del tipo tra il codice e il database. – phoog

+0

@phoog - Punto valido, immagino che ogni metodo abbia i suoi pro e contro. Personalmente, mi piace il mio DB e il codice il più strettamente possibile. Dovrei usare un 'int?' Nel codice, o non permettere 'null' nel database. –

6

Potete usare semplicemente Int32.Tryparse?

int number; 
bool result = Int32.TryParse(myDataReader["mycolumn"].ToString(), out number); 

Secondo il MSDN, number conterrà 0 se la conversione non è riuscita

+1

+1, buona risposta. Questo avrebbe funzionato, ma l'int che sto provando ad assegnare è una proprietà dell'oggetto che non funziona con 'out'. –

+5

Questo non si compila, vero? L'indicizzatore del lettore di dati restituisce un riferimento a un oggetto, TryParse utilizza un parametro stringa. Naturalmente, si potrebbe chiamare 'ToString' sull'oggetto, ma è piuttosto inefficiente costruire la rappresentazione di stringa di un int solo per poterlo analizzare. Una conversione unboxing sarebbe molto più efficace. – phoog

+0

@phoog, buon punto. Se è nulla, ToString bombarderebbe comunque. Potrebbe usare 'as string 'invece. –

10

Ecco un'altra opzione:

while (myDataReader.Read()) 
{ 
    myObject.intVal = (myDataReader["mycolumn"] as int? ?? 0); 
} 
+0

Il modo assolutamente migliore! Grazie Edyn. – ClownCoder

+0

Risposta brillante! Così pulito. –