Impossibile eseguire il cast di un oggetto di tipo "System.DBNull" per digitare "System.String"


109

Ho ricevuto l'errore di cui sopra nella mia app. Ecco il codice originale

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }

Ho sostituito con

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

C'è un modo migliore per aggirare questo?


2
dovresti davvero esaminare la risposta di @ rein, ti farà risparmiare un sacco di tempo a lungo termine
roman m

Risposte:


90

È possibile utilizzare una forma più breve:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()

EDIT: Non ho prestato attenzione a ExecuteScalar. Restituisce davvero null se il campo è assente nel risultato restituito. Quindi usa invece:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 

3
Ciò non funzionerà - "accountNumber" non è un valore di database ma una normale vecchia istanza di "oggetto" Plain Old .NET - è necessario verificare il normale valore "null". DBNull.Value funzionerebbe per un SqlDataReader o un SqlParameter, ma non per questo oggetto qui.
marc_s

Hai ragione, ho iniziato a ottimizzare la parte del controllo delle condizioni, non ho mai guardato la linea prima. Colpa mia.
Utente

C'è un errore di battitura nel tuo post che non posso davvero modificare perché la modifica richiede 6 caratteri per essere cambiata. Qualcuno può cambiare accountNumber.TosString () in accountNumber.ToString ()
Eric

@marc_s A seconda del layout db / query, è necessario controllare uno di essi o anche entrambi. Se WHERE non corrisponde a nessuna riga, otterrai un null, se la riga selezionata ha NULLin quella colonna, il valore restituito è System.DBNull.
Alexander

Nel primo caso @Alexander menziona -non corrisponde a nessuna riga- puoi fare affidamento su Convert.ToString o qualsiasi altro metodo Convert se stai bene con il valore che restituiscono durante la conversione da null: stringa vuota per le stringhe, 0 per i valori numerici, false per booleano, MinValue per DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime

199

Con una semplice funzione generica puoi renderlo molto facile. Basta fare questo:

return ConvertFromDBVal<string>(accountNumber);

utilizzando la funzione:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value)
    {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}

1
Sì, una funzione come questa è l'unica soluzione pratica. Qualsiasi tipo di logica in linea fallirà dopo averla copiata e incollata migliaia di volte. :-)
Christian Hayter

3
questo non funzionerà se provi a convertire 1 in bool (Convert.ToBoolean (1) funziona bene)
roman m

@roman: quindi vorremmo avere un controllo aggiuntivo (prima di verificare la presenza di null) che verifichi un tipo booleano ...
IAbstract

1
Se si desidera o è necessario utilizzare le funzioni di conversione, non funziona. Esistono diversi scenari in cui potresti preferire la conversione in un cast esplicito. @romanm ha notato uno di loro. Un altro è quando lavori con i decimali e ti preoccupi dei diversi meccanismi di arrotondamento che Convert.ToInt32 e (int) usano. Il primo arrotonda al valore pari più vicino, mentre il cast esplicito tronca semplicemente il valore: stackoverflow.com/questions/1608801/… Se possibile, eliminerei i NULL dal mix, usando la funzione T-SQL ISNULL
Jaime

2
@Jaime Questa funzione dovrebbe agire come un cast implicito da un tipo di dati SQL a un tipo di dati C # /. NET. Se hai bisogno di un cast esplicito, non usare questa funzione, fallo esplicitamente.
redini

17

ExecuteScalar tornerà

  • null se non è stato impostato alcun risultato
  • altrimenti la prima colonna della prima riga del gruppo di risultati, che potrebbe essere DBNull.

Se sai che la prima colonna del gruppo di risultati è una stringa, per coprire tutte le basi devi controllare sia null che DBNull. Qualcosa di simile a:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

Il codice precedente si basa sul fatto che DBNull.ToString restituisce una stringa vuota.

Se accountNumber fosse un altro tipo (diciamo intero), allora dovresti essere più esplicito:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

Se sai per certo che il tuo gruppo di risultati avrà sempre almeno una riga (ad es. SELEZIONA COUNT (*) ...), puoi saltare il controllo per null.

Nel tuo caso, il messaggio di errore "Impossibile eseguire il cast dell'oggetto di tipo" System.DBNull "per digitare" System.String "" indica che la prima colonna del set di risultati è un valore DBNUll. Questo è dal cast alla stringa sulla prima riga:

string accountNumber = (string) ... ExecuteScalar(...);

Il commento di Marc_s che non è necessario controllare per DBNull.Value è sbagliato.


il mio gruppo di risultati non restituirà sempre una riga.
Saif Khan

6

È possibile utilizzare l'operatore di coalescenza null di C #

return accountNumber ?? string.Empty;

-1: non verrà compilato: il metodo restituisce una stringa e accountNumber è un oggetto.
Joe

2
return Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya

return Cmd.ExecuteScalar (). ToString () ha fatto il lavoro per me
Taran

3

C'è un altro modo per risolvere questo problema. Che ne dici di modificare la procedura del tuo negozio? utilizzando la funzione sql ISNULL (il tuo campo ""), puoi restituire una stringa vuota se il valore restituito è null.

Quindi hai il tuo codice pulito come versione originale.


3

Questo è il metodo generico che utilizzo per convertire qualsiasi oggetto che potrebbe essere un DBNull.Value:

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

utilizzo:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

più breve:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);

2

Suppongo che tu possa farlo in questo modo:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

Se accountNumber è null significa che era DBNull non stringa :)


Oppure return (accountNumber as string) ?? string.Empty;, con accountNumber che è ancora un file object. Se si preferisce mantenere la chiamata al database sulla propria linea.
Brian il

1

String.Concat trasforma DBNull e valori null in una stringa vuota.

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

Tuttavia, penso che tu perda qualcosa sulla comprensibilità del codice


1
Cosa succede se scrivi return "" + accountNumber;?
Zev Spitz

0

Dato che ho un'istanza che non è nulla e se ho confrontato con DBNULL ho ottenuto Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' , e se ho provato a cambiare per confrontare con NULL, semplicemente non ha funzionato (poiché DBNull è un oggetto) anche questa è la risposta accettata.

Ho deciso di utilizzare semplicemente la parola chiave "is". Quindi il risultato è molto leggibile:

data = (item is DBNull) ? String.Empty : item


-1

Uso un'estensione per eliminare questo problema per me, che può essere o meno quello che stai cercando.

Funziona così:

public static class Extensions
{

    public String TrimString(this object item)
    {
        return String.Format("{0}", item).Trim();
    }

}

Nota:

Questa estensione non restituisce nullvalori! Se l'elemento è nullo DBNull.Value , restituirà una stringa vuota.

Uso:

public string GetCustomerNumber(Guid id)
{
    var obj = 
        DBSqlHelperFactory.ExecuteScalar(
            connectionStringSplendidmyApp, 
            CommandType.StoredProcedure, 
            "GetCustomerNumber", 
            new SqlParameter("@id", id)
        );
    return obj.TrimString();
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.