Nie można rzutować obiektu typu „System.DBNull” na typ „System.String”

109

Otrzymałem powyższy błąd w mojej aplikacji. Oto oryginalny kod

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

Zastąpiłem

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

Czy jest lepszy sposób obejścia tego?

Saif Khan
źródło
2
naprawdę powinieneś przyjrzeć się odpowiedzi @ rein, na dłuższą metę zaoszczędzisz dużo czasu
roman m

Odpowiedzi:

90

Można użyć krótszej formy:

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

EDYCJA: Nie zwróciłem uwagi na ExecuteScalar. W rzeczywistości zwraca wartość null, jeśli w wyniku zwracanym nie ma pola. Zamiast tego użyj:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 
Użytkownik
źródło
3
To nie zadziała - „accountNumber” nie jest wartością bazy danych, ale zwykłą starą instancją „obiektu” Plain Old .NET - należy porównać z normalną wartością „null”. DBNull.Value będzie działać dla SqlDataReader lub SqlParameter - ale nie dla tego obiektu tutaj.
marc_s
Masz rację, zacząłem optymalizować część sprawdzającą stan, wcześniej nie patrzyłem na linię. Mea culpa.
Użytkownik
W Twoim poście jest literówka, której tak naprawdę nie mogę edytować, ponieważ edycja wymaga zmiany 6 znaków. Czy ktoś może zmienić accountNumber.TosString () na accountNumber.ToString ()
Eric
@marc_s W zależności od układu db / query, musisz sprawdzić jeden z nich lub nawet oba. Jeśli GDZIE nie pasuje do żadnego wiersza, otrzymasz a null, jeśli wybrany wiersz ma NULLw tej kolumnie, wartość zwracana to System.DBNull.
Alexander
W pierwszym przypadku @Alexander wspomina -nie pasuje do żadnego wiersza- możesz polegać na Convert.ToString lub dowolnej innej metodzie Convert, jeśli nie masz nic przeciwko wartości, którą zwracają podczas konwersji z null: pusty ciąg dla ciągów, 0 dla wartości liczbowych, fałsz for boolean, MinValue for DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime
199

Dzięki prostej funkcji ogólnej możesz to bardzo ułatwić. Po prostu zrób to:

return ConvertFromDBVal<string>(accountNumber);

za pomocą funkcji:

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;
    }
}
wodza
źródło
1
Tak, taka funkcja to jedyne praktyczne rozwiązanie. Każda logika wbudowana nie powiedzie się po skopiowaniu i wklejeniu go tysiące razy. :-)
Christian Hayter
3
to nie zadziała, jeśli spróbujesz przekonwertować 1 na bool (Convert.ToBoolean (1) działa dobrze, chociaż)
roman m
@roman: więc chcielibyśmy mieć dodatkową kontrolę (przed sprawdzeniem wartości null), która sprawdza typ boolowski ...
IAbstract
1
Jeśli chcesz lub musisz użyć funkcji Convert, to nie działa. Istnieje kilka scenariuszy, w których możesz preferować konwersję do jawnej obsady. @romanm zauważył jedną z nich. Innym jest, gdy pracujesz z liczbami dziesiętnymi i dbasz o różne mechanizmy zaokrąglania, których używają Convert.ToInt32 i (int). Pierwsza zaokrągla do najbliższej parzystej wartości, podczas gdy jawne rzutowanie po prostu obcina wartość: stackoverflow.com/questions/1608801/… Jeśli to możliwe, wyeliminowałbym wartości NULL z miksu, używając funkcji T-SQL ISNULL
Jaime
2
@Jaime Ta funkcja ma działać jak niejawne rzutowanie z typu danych SQL na typ danych C # / .NET. Jeśli potrzebujesz jawnego rzutowania, nie używaj tej funkcji - zrób to jawnie.
wodnik
17

ExecuteScalar zwróci

  • null, jeśli nie ma zestawu wyników
  • w przeciwnym razie pierwsza kolumna pierwszego wiersza zestawu wyników, która może mieć wartość DBNull.

Jeśli wiesz, że pierwsza kolumna zestawu wyników jest łańcuchem, to aby objąć wszystkie zasady, musisz sprawdzić zarówno null, jak i DBNull. Coś jak:

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

Powyższy kod opiera się na fakcie, że DBNull.ToString zwraca pusty ciąg.

Gdyby numer konta był innego typu (powiedzmy liczbą całkowitą), musiałbyś być bardziej precyzyjny:

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

Jeśli wiesz na pewno, że Twój zbiór wyników zawsze będzie zawierał co najmniej jeden wiersz (np. SELECT COUNT (*) ...), możesz pominąć sprawdzanie wartości null.

W Twoim przypadku komunikat o błędzie „Nie można rzutować obiektu typu„ System.DBNull ”na typ„ System.String ”oznacza, że ​​pierwsza kolumna zestawu wyników jest wartością DBNUll. To jest od rzutu do łańcucha w pierwszej linii:

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

Komentarz Marc_s, że nie musisz sprawdzać DBNull.Value, jest błędny.

Joe
źródło
mój zestaw wyników nie zawsze zwraca wiersz.
Saif Khan
6

Można użyć operatora łączenia wartości null w języku C #

return accountNumber ?? string.Empty;
Nathan Koop
źródło
-1: To się nie skompiluje: metoda zwraca ciąg znaków, a accountNumber jest obiektem.
Joe
2
return Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya,
return Cmd.ExecuteScalar (). ToString () wykonał zadanie za mnie
Taran
3

Istnieje inny sposób obejścia tego problemu. Co powiesz na zmodyfikowanie procedury sklepu? używając funkcji ISNULL (twoje pole, "") sql, możesz zwrócić pusty ciąg, jeśli wartość zwracana jest równa null.

Następnie masz czysty kod jako oryginalną wersję.

Russel Yang
źródło
3

Oto ogólna metoda, której używam do konwersji dowolnego obiektu, który może być DBNull.Value:

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

stosowanie:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

krótszy:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);
Heras
źródło
2

Przypuszczam, że możesz to zrobić w ten sposób:

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

Jeśli accountNumber ma wartość null, oznacza to, że był to DBNull, a nie łańcuch :)

ppiotrowicz
źródło
Lub return (accountNumber as string) ?? string.Empty;, gdy accountNumber nadal będzie object. Jeśli wolisz, aby Twoja baza danych znajdowała się na osobnej linii.
Brian
1

String.Concat przekształca wartości DBNull i null na pusty ciąg.

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

    return String.Concat(accountNumber);

 }

Myślę jednak, że tracisz coś na zrozumiałości kodu

Andrea Parodi
źródło
1
Co się stanie, jeśli napiszesz return "" + accountNumber;?
Zev Spitz,
0

Odkąd mam instancję, która nie jest null i jeśli porównuję z DBNULL, otrzymałem Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' wyjątek, a gdybym spróbował zmienić, aby porównać do NULL, po prostu nie działało (ponieważ DBNull jest obiektem), nawet to jest akceptowana odpowiedź.

Postanowiłem po prostu użyć słowa kluczowego „is”. Wynik jest więc bardzo czytelny:

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

Remy
źródło
-1

Używam rozszerzenia, aby wyeliminować ten problem za mnie, który może, ale nie musi, być tym, czego szukasz.

To wygląda tak:

public static class Extensions
{

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

}

Uwaga:

To rozszerzenie nie zwraca nullwartości! Jeśli elementem jest nulllub DBNull.Value , zwróci pusty ciąg.

Stosowanie:

public string GetCustomerNumber(Guid id)
{
    var obj = 
        DBSqlHelperFactory.ExecuteScalar(
            connectionStringSplendidmyApp, 
            CommandType.StoredProcedure, 
            "GetCustomerNumber", 
            new SqlParameter("@id", id)
        );
    return obj.TrimString();
}
jp2code
źródło
-2

Konwertuj jak

string s = System.DBNull.value.ToString();
Sudhakar Rao
źródło