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?


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

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

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.


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;

-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ę.


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

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 :)


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


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


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.