SQL Data Reader - обработка значений Null column

Я использую SQLdatareader для создания POCOs из базы данных. Код работает, кроме случаев, когда он встречает нулевое значение в базе данных. Например, если столбец FirstName в базе данных содержит нулевое значение, генерируется исключение.

employee.FirstName = sqlreader.GetString(indexFirstName);

Каков наилучший способ обработки нулевых значений в этой ситуации?

Ответ 1

Вам нужно проверить IsDBNull:

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

Это ваш единственный надежный способ обнаружить и обработать эту ситуацию.

Я завернул эти вещи в методы расширения и, как правило, возвратил значение по умолчанию, если столбец действительно null:

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

Теперь вы можете вызвать его так:

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

и вам больше не придется беспокоиться об исключении или значении null.

Ответ 2

Вы должны использовать оператор as в сочетании с оператором ?? для значений по умолчанию. Типы значений должны считаться нулевыми и задаваться по умолчанию.

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

Оператор as обрабатывает кастинг, включая проверку для DBNull.

Ответ 3

Для строки вы можете просто указать версию объекта (доступ к которой используется с помощью оператора массива) и завершить нулевую строку для нулей:

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

или

employee.FirstName = sqlreader[indexFirstName] as string;

Для целых чисел, если вы применяете к nullable int, вы можете использовать GetValueOrDefault()

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

или оператор нуль-коалесцирования (??).

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

Ответ 4

IsDbNull(int) обычно намного медленнее, чем при использовании таких методов, как GetSqlDateTime, а затем сравнивается с DBNull.Value. Попробуйте эти методы расширения для 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;
}

Используйте их следующим образом:

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

Ответ 5

Один из способов сделать это - проверить нулевые значения db:

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

Ответ 6

Я не думаю, что значение столбца NULL, когда строки возвращаются в datareader с использованием имени столбца.

Если вы выполняете datareader["columnName"].ToString();, он всегда будет давать вам значение, которое может быть пустой строкой (String.Empty, если вам нужно сравнить).

Я бы использовал следующее и не слишком беспокоился:

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

Ответ 7

reader.IsDbNull(ColumnIndex) работает так, как говорят многие.

И я хочу упомянуть, если вы работаете с именами столбцов, просто сравнение типов может быть более удобным.

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

Ответ 8

Это решение менее зависимо от поставщика и работает с SQL, OleDB и 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

Я стараюсь заменить нулевые значения в инструкции SELECT чем-то соответствующим.

SELECT ISNULL(firstname, '') FROM people

Здесь я заменяю каждый пустой пустой строкой. В этом случае ваш код не будет ошибочным.

Ответ 10

Проверьте sqlreader.IsDBNull(indexFirstName), прежде чем пытаться его прочитать.

Ответ 11

Вы можете написать функцию Generic, чтобы проверить Null и включить значение по умолчанию, когда оно равно NULL. Вызовите это при чтении Datareader

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

При чтении Datareader используйте

                        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"]);
                         }

Ответ 12

Я думаю, вы бы хотели использовать:

SqlReader.IsDBNull(indexFirstName)

Ответ 13

как насчет создания вспомогательных методов

Для строки

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

        return o.ToString();
    }

использование

MyStringConverter(read["indexStringValue"])

Для Int

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

        return Convert.ToInt32(o);
    }

использование

MyIntonverter(read["indexIntValue"])

Для даты

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

использование

MyDateConverter(read["indexDateValue"])

Примечание: для DateTime объявите varialbe как

DateTime? variable;

Ответ 14

В дополнение к ответу от marc_s вы можете использовать более общий метод расширения для получения значений из SqlDataReader:

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

Ответ 15

Мы используем серию статических методов, чтобы вытащить все значения из наших считывателей данных. Таким образом, в этом случае мы будем называть DBUtils.GetString(sqlreader(indexFirstName)). Преимущество создания статических/общих методов заключается в том, что вам не нужно делать одни и те же проверки снова и снова...

Статический метод будет содержать код для проверки нулей (см. другие ответы на этой странице).

Ответ 16

Вы можете использовать условный оператор:

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

Ответ 17

и/или использовать тернарный оператор с назначением:

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

заменить значение по умолчанию (когда null), соответствующее каждому типу свойств...

Ответ 18

Я использую приведенный ниже код для обработки нулевых ячеек на листе Excel, который читается в datatable.

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

Ответ 19

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

Ответ 20

Этот метод зависит от indexFirstName, который должен быть порядковым номером столбца на основе нуля.

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

Если вы не знаете индекс столбца, но не хотите проверять имя, вы можете использовать этот метод расширения вместо:

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

И используйте метод следующим образом:

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

Ответ 21

Старый вопрос, но, возможно, кому-то еще нужен ответ

на самом деле я работал над этим вопросом так

Для 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;
    }

то же самое для строки просто вернуть "" вместо 0, так как "" - пустая строка

так что вы можете использовать его как

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

а также

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

очень гибкий, поэтому вы можете вставить любой запрос, чтобы прочитать любой столбец, и он никогда не вернется с ошибкой

Ответ 22

Используя ответ из getpsyched, я создал универсальный метод, который проверяет значение столбца по его имени.

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

Использование:

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

Ответ 23

Вот вспомогательный класс, который могут использовать другие, если им нужно, основываясь на ответе @marc_s:

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

Ответ 24

Конвертировать ручки DbNull разумно.

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

Ответ 25

Здесь есть много ответов с полезной информацией (и некоторой неправильной информацией), я хотел бы собрать все это вместе.

Короткий ответ на вопрос - проверить DBNull - с этим согласны почти все :)

Вместо того, чтобы использовать вспомогательный метод для чтения значений NULL для каждого типа данных SQL, универсальный метод позволяет нам решить эту проблему с помощью гораздо меньшего количества кода. Однако у вас не может быть единого универсального метода для типов значений Nullable и ссылочных типов, это подробно обсуждается в типе Nullable как возможный универсальный параметр? и ограничение общего типа С# для всего, что можно обнулять.

Итак, исходя из ответов @ZXX и @getpsyched, мы в итоге получаем 2 метода для получения значений, допускающих значение NULL, и я добавил 3-й для ненулевых значений (он завершает набор на основе именования методов).

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

Я обычно использую имена столбцов, меняйте их, если вы используете индексы столбцов. Основываясь на названиях этих методов, я могу сказать, ожидаю ли я, что данные будут обнуляемыми или нет, что весьма полезно при просмотре кода, написанного давным-давно.

Подсказки;

  • Отсутствие обнуляемых столбцов в базе данных позволяет избежать этой проблемы. Если у вас есть контроль над базой данных, столбцы должны быть ненулевыми по умолчанию и обнуляться только при необходимости.
  • Не приводите значения базы данных с помощью оператора С# "как", потому что, если приведение выполнено неправильно, оно молча вернет ноль.
  • Использование выражения значения по умолчанию изменит нулевые значения базы данных на ненулевые значения для типов значений, таких как int, datetime, bit и т.д.

Наконец, во время тестирования вышеуказанных методов для всех типов данных SQL Server, которые я обнаружил, вы не можете напрямую получить char [] из SqlDataReader, если вам нужен char [], вам нужно будет получить строку и использовать ToCharArray().

Ответ 26

вы также можете проверить это,

if(null !=x && x.HasRows)
{ ....}