SQL Data Reader-обработка значений пустых столбцов


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

employee.FirstName = sqlreader.GetString(indexFirstName);

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

22 241

22 ответа:

вы должны проверить для 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 снова значение.

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

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

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

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

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

или

employee.FirstName = sqlreader[indexFirstName] as string;

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

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

или оператор null-coalescing (??).

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

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

один из способов сделать это-проверить наличие нулей БД:

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

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

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

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

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

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

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

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

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

то, что я склонен делать, это заменить нулевые значения в инструкции SELECT чем-то подходящим.

SELECT ISNULL(firstname, '') FROM people

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

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

Вы можете написать универсальную функцию для проверки 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"]);
                         }

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

SqlReader.IsDBNull(indexFirstName)

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

Строка

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;

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

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

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

в реальном я работал вокруг этой проблемы, как это

для 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;

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

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

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}
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();
        }

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

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

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

этот метод зависит от 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"];
}

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

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

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

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

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

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