web-dev-qa-db-de.com

SQL Data Reader - Umgang mit Nullspaltenwerten

Ich verwende einen SQLDatareader, um POCOs aus einer Datenbank zu erstellen. Der Code funktioniert nur, wenn er auf einen Nullwert in der Datenbank trifft. Wenn die Spalte FirstName in der Datenbank beispielsweise einen Nullwert enthält, wird eine Ausnahme ausgelöst. 

employee.FirstName = sqlreader.GetString(indexFirstName);

Was ist der beste Weg, um mit Nullwerten in dieser Situation umzugehen?

251
DenaliHardtail

Sie müssen nach IsDBNull suchen:

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Nur so können Sie diese Situation zuverlässig erkennen und handhaben.

Ich habe diese Dinge in Erweiterungsmethoden verpackt und neige dazu, einen Standardwert zurückzugeben, wenn die Spalte tatsächlich null ist:

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

Jetzt kannst du es so nennen:

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

und Sie müssen sich nie wieder um eine Ausnahme oder einen null-Wert kümmern.

390
marc_s

Sie sollten den Operator as in Kombination mit dem Operator ?? für Standardwerte verwenden. Werttypen müssen als nullfähig gelesen und mit einem Standardwert versehen werden.

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

Der Operator as übernimmt das Casting einschließlich der Prüfung auf DBNull.

211
stevehipwell

Für eine Zeichenfolge können Sie einfach die Objektversion umwandeln (auf die mit dem Array-Operator zugegriffen wird) und mit einer Null-Zeichenfolge für Nullen abschließen:

employee.FirstName = (string)sqlreader[indexFirstName];

oder

employee.FirstName = sqlreader[indexFirstName] as string;

Bei Ganzzahlen können Sie GetValueOrDefault () verwenden, wenn Sie in ein nullfähiges int konvertieren.

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();

oder der nullkoaleszierende Operator (??).

employee.Age = (sqlreader[indexAge] as int?) ?? 0;
27
Gone Coding

IsDbNull(int) ist normalerweise viel langsamer als Methoden wie GetSqlDateTime und den Vergleich mit DBNull.Value. Versuchen Sie diese Erweiterungsmethoden für SqlDataReader.

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

Verwenden Sie sie wie folgt:

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);
18
ZXX

Eine Möglichkeit, dies zu tun, besteht darin, nach DB-Nullen zu suchen:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));
11
Michael Todd

Ich glaube nicht, dass es einenNULLcolumn-Wert gibt, wenn Zeilen innerhalb eines Datenbereichs mit dem Spaltennamen zurückgegeben werden.

Wenn Sie datareader["columnName"].ToString(); tun, erhalten Sie immer einen Wert, der eine leere Zeichenfolge sein kann (String.Empty, wenn Sie vergleichen müssen).

Ich würde folgendes verwenden und würde mir nicht zu viele Sorgen machen:

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
10
el bayames

reader.IsDbNull(ColumnIndex) funktioniert, wie viele Antworten sagen.

Wenn Sie mit Spaltennamen arbeiten, ist es möglicherweise einfacher, nur Typen zu vergleichen.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
10
PJ3

Diese Lösung ist weniger herstellerabhängig und funktioniert mit einem SQL-, OleDB- und MySQL-Reader:

public static string GetStringSafe(this IDataReader reader, int colIndex)
{
    return GetStringSafe(reader, colIndex, string.Empty);
}

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
    if (!reader.IsDBNull(colIndex))
        return reader.GetString(colIndex);
    else
        return defaultValue;
}

public static string GetStringSafe(this IDataReader reader, string indexName)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName));
}

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}
9
Summer-Time

Ich neige dazu, die Nullwerte in der SELECT-Anweisung durch etwas zu ersetzen.

SELECT ISNULL(firstname, '') FROM people

Hier ersetze ich jede Null durch eine leere Zeichenfolge. Ihr Code wird in diesem Fall nicht fehlerhaft ausgegeben.

7
alex

Überprüfen Sie sqlreader.IsDBNull(indexFirstName), bevor Sie versuchen, es zu lesen.

6
CesarGon

Sie können eine generische Funktion zum Überprüfen von Null schreiben und den Standardwert einschließen, wenn es NULL ist. Rufen Sie dies beim Lesen des Datenbereichs auf

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }

Beim Lesen des Datenbereichs verwenden

                        while (dr.Read())
                        {
                            tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
                            Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
                            Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
                            Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
                         }
5
Vijai

Ich denke, du würdest verwenden wollen:

SqlReader.IsDBNull(indexFirstName)
3
bytebender

so erstellen Sie Hilfsmethoden

Für String

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

Verwendungszweck

MyStringConverter(read["indexStringValue"])

Für Int

 private static int MyIntonverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return 0;

        return Convert.ToInt32(o);
    }

Verwendungszweck

MyIntonverter(read["indexIntValue"])

Für Datum

private static DateTime? MyDateConverter(object o)
    {
        return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
    }

Verwendungszweck

MyDateConverter(read["indexDateValue"])

Hinweis: Für DateTime deklarieren Sie varialbe as

DateTime? variable;
3
Usman Ali

Wir verwenden eine Reihe statischer Methoden, um alle Werte aus unseren Datenlesern zu ziehen. In diesem Fall würden wir also DBUtils.GetString(sqlreader(indexFirstName)) aufrufen. Der Vorteil der Erstellung statischer/gemeinsam genutzter Methoden besteht darin, dass Sie nicht immer und immer wieder die gleichen Prüfungen durchführen müssen ...

Die statischen Methoden enthalten Code, um nach Nullen zu suchen (siehe andere Antworten auf dieser Seite).

2
Sonny Boy

Sie können den bedingten Operator verwenden:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
2
Panayot Minkov

Alte Frage, aber vielleicht braucht jemand noch eine Antwort 

in Wirklichkeit habe ich so um dieses Problem gearbeitet

Für int:

public static object GatDataInt(string Query, string Column)
    {
        SqlConnection DBConn = new SqlConnection(ConnectionString);
        if (DBConn.State == ConnectionState.Closed)
            DBConn.Open();
        SqlCommand CMD = new SqlCommand(Query, DBConn);
        SqlDataReader RDR = CMD.ExecuteReader();
        if (RDR.Read())
        {
            var Result = RDR[Column];
            RDR.Close();
            DBConn.Close();
            return Result;
        }
        return 0;
    }

das gleiche für string gibt einfach "" anstelle von 0 zurück, da "" leere Zeichenfolge ist

so kannst du es gerne benutzen 

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;

und

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;

sehr flexibel, so dass Sie jede Abfrage einfügen können, um eine Spalte zu lesen, und es wird nie ein Fehler angezeigt

2
Ahmed M.Kamal

und/oder ternären Operator mit Zuordnung verwenden:

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
                     String.Empty: rdr.GetString(indexFirstName);

ersetzen Sie den Standardwert (wenn Null) für jeden Eigenschaftstyp ...

1
Charles Bretana
private static void Render(IList<ListData> list, IDataReader reader)
        {
            while (reader.Read())
            {

                listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
                //没有这一列时,让其等于null
                list.Add(listData);
            }
            reader.Close();
        }
1
xux

Als Ergänzung zur Antwort von marc_s können Sie eine allgemeinere Erweiterungsmethode verwenden, um Werte aus dem SqlDataReader-Objekt abzurufen:

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }
1
getpsyched

Ich verwende den unten aufgelisteten Code, um Nullzellen in einer Excel-Tabelle zu behandeln, die in eine Datentabelle eingelesen wird.

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}
1
Tequila

Diese Methode hängt von indexFirstName ab, der die auf Null basierende Spaltenordnung sein sollte.

if(!sqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Wenn Sie den Spaltenindex nicht kennen, aber einen Namen überprüfen möchten, können Sie stattdessen diese Erweiterungsmethode verwenden:

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}

Und wende die Methode so an:

if(sqlReader.HasColumn("FirstName"))
{
  employee.FirstName = sqlreader["FirstName"];
}
1
Ogglas

Durch Beeinflussung der Antwort von getpsyched habe ich eine generische Methode erstellt, die den Spaltenwert anhand seines Namens überprüft

public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
  var indexOfColumn = reader.GetOrdinal(nameOfColumn);
  return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}

Verwendungszweck: 

var myVariable = SafeGet<string>(reader, "NameOfColumn")
0
Project Mayhem

Hier gibt es viele Antworten mit nützlichen Informationen (und einigen falschen Informationen), ich möchte alles zusammenbringen.

Die kurze Antwort auf die Frage lautet, nach DBNull zu suchen - fast jeder ist sich in diesem Punkt einig :)

Anstatt eine Hilfsmethode zum Lesen von nullwertfähigen Werten pro SQL-Datentyp zu verwenden, können wir diese mit einer generischen Methode mit viel weniger Code beheben. Sie können jedoch nicht eine einzige generische Methode sowohl für nullfähige Werttypen als auch für Referenztypen verwenden. Dies wird ausführlich in Nullfähiger Typ als generischer Parameter beschrieben. und C # generische Typeinschränkung für alles Nullable .

Nach den Antworten von @ZXX und @getpsyched erhalten wir also 2 Methoden, um nullbare Werte zu erhalten, und ich habe eine dritte für Nicht-Null-Werte hinzugefügt (sie vervollständigt die Menge auf der Grundlage der Methodennamen).

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

Ich verwende normalerweise Spaltennamen, ändern Sie diese, wenn Sie Spaltenindizes verwenden. Anhand dieser Methodennamen kann ich erkennen, ob ich erwarte, dass die Daten nullwertfähig sind oder nicht, was bei der Betrachtung von Code, der vor langer Zeit geschrieben wurde, sehr nützlich ist.

Tipps;

  • Das Fehlen nullfähiger Spalten in der Datenbank vermeidet dieses Problem. Wenn Sie die Kontrolle über die Datenbank haben, sollten die Spalten standardmäßig nicht null sein und nur bei Bedarf null sein.
  • Konvertieren Sie keine Datenbankwerte mit dem Operator C # 'as', da bei einer falschen Konvertierung im Hintergrund null zurückgegeben wird.
  • Die Verwendung eines Standardwertausdrucks ändert die Datenbanknullwerte in Nicht-Nullwerte für Werttypen wie int, datetime, bit usw.

Beim Testen der oben genannten Methoden für alle SQL Server-Datentypen habe ich festgestellt, dass Sie ein Zeichen [] nicht direkt aus einem SqlDataReader abrufen können. Wenn Sie ein Zeichen [] möchten, müssen Sie eine Zeichenfolge abrufen und ToCharArray () verwenden.

0
Rhys Jones

Hier ist eine Hilfsklasse, die andere verwenden können, wenn sie auf der Antwort von @marc_s basieren:

public static class SQLDataReaderExtensions
    {
        public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
        }

        public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as int?;
        }

        public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
        }

        public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as DateTime?;
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
        {
            return SafeGetBoolean(dataReader, fieldName, false);
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
        }
    }
0
Raghav

Handles DbNull sinnvoll konvertieren.

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));
0
Frank Hagenson