SQL Data Reader-обработка значений пустых столбцов
Я использую SQLdatareader для построения POCOs из базы данных. Код работает, за исключением случаев, когда он встречает нулевое значение в базе данных. Например, если столбец FirstName в базе данных содержит значение null, возникает исключение.
employee.FirstName = sqlreader.GetString(indexFirstName);
каков наилучший способ обработки нулевых значений в этой ситуации?
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 пустой строкой. Ваш код не будет выдавать ошибку в этом случае.
Вы можете написать универсальную функцию для проверки 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"]); }
как о создании вспомогательных методов
Строка
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() : "";