Objekt vom Typ 'System.DBNull' kann nicht in 'System.String' umgewandelt werden

109

Ich habe den obigen Fehler in meiner App erhalten. Hier ist der Originalcode

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

Ich ersetzte durch

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

Gibt es einen besseren Weg, um das zu umgehen?

Saif Khan
quelle
2
Sie sollten sich wirklich die Antwort von @ rein ansehen, um auf lange Sicht viel Zeit zu sparen
Roman m

Antworten:

89

Eine kürzere Form kann verwendet werden:

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

BEARBEITEN: ExecuteScalar wurde nicht beachtet. Es gibt wirklich null zurück, wenn das Feld im Rückgabeergebnis fehlt. Verwenden Sie stattdessen:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 
Benutzer
quelle
3
Das wird nicht funktionieren - die "accountNumber" ist kein Datenbankwert, sondern eine reguläre alte Plain Old .NET "Objekt" -Instanz - Sie müssen sie mit dem normalen "Null" -Wert vergleichen. Der DBNull.Value würde für einen SqlDataReader oder einen SqlParameter funktionieren - aber nicht für dieses Objekt hier.
marc_s
Sie haben Recht, ich habe angefangen, den Zustandstest zu optimieren, habe mir die Linie noch nie angesehen. Mea culpa.
Benutzer
Es gibt einen Tippfehler in Ihrem Beitrag, den ich nicht wirklich bearbeiten kann, da für die Bearbeitung 6 Zeichen geändert werden müssen. Kann jemand accountNumber.TosString () in accountNumber.ToString () ändern
Eric
@marc_s Abhängig vom Datenbank- / Abfragelayout müssen Sie einen von beiden oder sogar beide prüfen. Wenn das WHERE keiner Zeile entspricht, erhalten Sie eine null. Wenn die ausgewählte Zeile NULLin dieser Spalte enthalten ist, lautet der Rückgabewert System.DBNull.
Alexander
Im ersten Fall erwähnt @Alexander - keine Übereinstimmung mit einer Zeile -, dass Sie sich auf Convert.ToString oder eine andere Convert-Methode verlassen können, wenn Sie mit dem Wert, den sie beim Konvertieren von null zurückgeben, einverstanden sind: leere Zeichenfolge für Zeichenfolgen, 0 für numerische Werte, false für boolean, MinValue für DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime
199

Mit einer einfachen generischen Funktion können Sie dies sehr einfach machen. Mach das einfach:

return ConvertFromDBVal<string>(accountNumber);

mit der Funktion:

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;
    }
}
Zügel
quelle
1
Ja, eine solche Funktion ist die einzige praktische Lösung. Jede Art von Inline-Logik schlägt fehl, nachdem Sie sie tausendmal kopiert und eingefügt haben. :-)
Christian Hayter
3
Dies wird nicht funktionieren, wenn Sie versuchen, 1 in Bool zu konvertieren (Convert.ToBoolean (1) funktioniert gut tho)
Roman m
@ Roman: Also dann möchten wir eine zusätzliche Prüfung (vor der Prüfung auf Null), die nach einem booleschen Typ
sucht
1
Wenn Sie Konvertierungsfunktionen verwenden möchten oder müssen, funktioniert dies nicht. Es gibt verschiedene Szenarien, in denen Sie möglicherweise die Konvertierung in eine explizite Besetzung bevorzugen. @ Romanm bemerkte einen von ihnen. Eine andere Möglichkeit besteht darin, mit Dezimalstellen zu arbeiten und sich um die verschiedenen Rundungsmechanismen zu kümmern, die Convert.ToInt32 und (int) verwenden. Ersteres rundet auf den nächsten geraden Wert, während die explizite Umwandlung nur den Wert abschneidet : stackoverflow.com/questions/1608801/… Wenn möglich, würde ich NULL-Werte mit der T-SQL ISNULL-Funktion aus dem Mix entfernen
Jaime,
2
@Jaime Diese Funktion soll sich wie eine implizite Umwandlung von einem SQL-Datentyp in einen C # /. NET-Datentyp verhalten. Wenn Sie eine explizite Besetzung benötigen, verwenden Sie diese Funktion nicht, sondern explizit.
Zügel
17

ExecuteScalar wird zurückgegeben

  • null, wenn keine Ergebnismenge vorhanden ist
  • Andernfalls die erste Spalte der ersten Zeile der Ergebnismenge, die DBNull sein kann.

Wenn Sie wissen, dass die erste Spalte der Ergebnismenge eine Zeichenfolge ist, müssen Sie zur Abdeckung aller Basen sowohl auf null als auch auf DBNull prüfen. Etwas wie:

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

Der obige Code basiert auf der Tatsache, dass DBNull.ToString eine leere Zeichenfolge zurückgibt.

Wenn accountNumber ein anderer Typ wäre (z. B. Ganzzahl), müssten Sie expliziter sein:

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

Wenn Sie sicher sind, dass Ihre Ergebnismenge immer mindestens eine Zeile enthält (z. B. SELECT COUNT (*) ...), können Sie die Prüfung auf Null überspringen.

In Ihrem Fall gibt die Fehlermeldung "Objekt vom Typ 'System.DBNull' kann nicht in Typ 'System.String' umgewandelt werden" an, dass die erste Spalte Ihrer Ergebnismenge ein DBNUll-Wert ist. Dies ist von der Besetzung bis zur Zeichenfolge in der ersten Zeile:

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

Marc_s 'Kommentar, dass Sie nicht nach DBNull.Value suchen müssen, ist falsch.

Joe
quelle
Meine Ergebnismenge gibt nicht immer eine Zeile zurück.
Saif Khan
6

Sie können den Null-Koaleszenz-Operator von C # verwenden

return accountNumber ?? string.Empty;
Nathan Koop
quelle
-1: Das wird nicht kompiliert: Die Methode gibt eine Zeichenfolge zurück und accountNumber ist ein Objekt.
Joe
2
return Cmd.ExecuteScalar (). ToString () ?? String.Empty;
Chaitanya
return Cmd.ExecuteScalar (). ToString () hat die Arbeit für mich erledigt
Taran
3

Es gibt eine andere Möglichkeit, dieses Problem zu umgehen. Wie wäre es mit einer Änderung Ihrer Geschäftsabläufe? Mit der SQL-Funktion ISNULL (Ihr Feld, "") können Sie eine leere Zeichenfolge zurückgeben, wenn der Rückgabewert null ist.

Dann haben Sie Ihren sauberen Code als Originalversion.

Russel Yang
quelle
3

Dies ist die generische Methode, mit der ich jedes Objekt konvertiere, das möglicherweise ein DBNull.Value ist:

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

Verwendung:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

kürzer:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);
Heras
quelle
2

Ich nehme an, Sie können es so machen:

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

Wenn accountNumber null ist, bedeutet dies, dass DBNull keine Zeichenfolge war :)

ppiotrowicz
quelle
Oder return (accountNumber as string) ?? string.Empty;wenn accountNumber immer noch ein ist object. Wenn Sie Ihren Datenbankaufruf lieber auf einer eigenen Leitung halten möchten.
Brian
1

String.Concat wandelt DBNull- und Nullwerte in eine leere Zeichenfolge um.

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

    return String.Concat(accountNumber);

 }

Ich denke jedoch, dass Sie etwas an Code-Verständlichkeit verlieren

Andrea Parodi
quelle
1
Was passiert, wenn du schreibst return "" + accountNumber;?
Zev Spitz
0

Da ich eine Instanz habe, die nicht null ist und wenn ich mit DBNULL vergleiche, habe ich Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' Ausnahme erhalten, und wenn ich versucht habe, zu ändern, um mit NULL zu vergleichen, hat es einfach nicht funktioniert (da DBNull ein Objekt ist), auch wenn dies die akzeptierte Antwort ist.

Ich habe mich entschieden, einfach das Schlüsselwort 'is' zu verwenden. Das Ergebnis ist also sehr gut lesbar:

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

Remy
quelle
-1

Ich verwende eine Erweiterung, um dieses Problem für mich zu beseitigen, das möglicherweise das ist, wonach Sie suchen.

Es geht so:

public static class Extensions
{

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

}

Hinweis:

Diese Erweiterung nicht zurückgeben nullWerte! Wenn das Element nulloder DBNull.Value ist , wird es einen leeren String zurück.

Verwendung:

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

Konvertieren Sie es wie

string s = System.DBNull.value.ToString();
Sudhakar Rao
quelle