SQL Data Reader - handling Null column values
我正在使用sqldatareader从数据库构建POCO。除非在数据库中遇到空值,否则代码可以工作。例如,如果数据库中的firstname列包含空值,则会引发异常。
1 | employee.FirstName = sqlreader.GetString(indexFirstName); |
在这种情况下,处理空值的最佳方法是什么?
您需要检查
1 2 3 4 | if(!SqlReader.IsDBNull(indexFirstName)) { employee.FirstName = sqlreader.GetString(indexFirstName); } |
这是检测和处理这种情况的唯一可靠方法。
我将这些东西包装成扩展方法,如果列确实是
1 2 3 4 5 6 | public static string SafeGetString(this SqlDataReader reader, int colIndex) { if(!reader.IsDBNull(colIndex)) return reader.GetString(colIndex); return string.Empty; } |
现在你可以这样称呼它:
1 | employee.FirstName = SqlReader.SafeGetString(indexFirstName); |
你再也不必担心异常或
您应该使用
1 2 | employee.FirstName = sqlreader[indexFirstName] as string; employee.Age = sqlreader[indexAge] as int? ?? default(int); |
对于字符串,您可以简单地强制转换对象版本(使用数组运算符访问),并使用空字符串结束空值:
1 | employee.FirstName = (string)sqlreader[indexFirstName]; |
或
1 | employee.FirstName = sqlreader[indexFirstName] as string; |
对于整数,如果强制转换为可为空的int,则可以使用getValueOrDefault()。
1 | employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault(); |
或空合并运算符(
1 | employee.Age = (sqlreader[indexAge] as int?) ?? 0; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } |
像这样使用它们:
1 2 3 | var dd = r.Val<DateTime>(ords[4]); var ii = r.Def<int>(ords[0]); int nn = r.Def<int>(ords[0]); |
一种方法是检查db nulls:
1 2 3 | employee.FirstName = (sqlreader.IsDBNull(indexFirstName) ?"" : sqlreader.GetString(indexFirstName)); |
当使用列名在DataReader中返回行时,我认为没有空列值。
如果您执行
我会使用以下内容,不会担心太多:
1 | employee.FirstName = sqlreader["columnNameForFirstName"].ToString(); |
我想说一下,如果您使用列名,只比较类型可能会更舒服。
1 |
此解决方案不依赖于供应商,可用于SQL、OLEDB和MySQL读卡器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } |
我倾向于用适当的东西替换select语句中的空值。
1 | SELECT ISNULL(firstname, '') FROM people |
这里我用一个空字符串替换每个空值。在这种情况下,您的代码不会出错。
在你试图阅读之前,先检查一下
您可以编写一个通用函数来检查空值,并在其为空时包含默认值。读取DataReader时调用此函数
1 2 3 4 | public T CheckNull<T>(object obj) { return (obj == DBNull.Value ? default(T) : (T)obj); } |
读取数据读取器时,请使用
1 2 3 4 5 6 7 | 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"]); } |
我想你应该使用:
1 | SqlReader.IsDBNull(indexFirstName) |
如何创建助手方法
弦乐器
1 2 3 4 5 6 7 | private static string MyStringConverter(object o) { if (o == DBNull.Value || o == null) return""; return o.ToString(); } |
用法
1 | MyStringConverter(read["indexStringValue"]) |
对于int
1 2 3 4 5 6 7 | private static int MyIntonverter(object o) { if (o == DBNull.Value || o == null) return 0; return Convert.ToInt32(o); } |
用法
1 | MyIntonverter(read["indexIntValue"]) |
为了约会
1 2 3 4 | private static DateTime? MyDateConverter(object o) { return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o); } |
用法
1 | MyDateConverter(read["indexDateValue"]) |
注意:对于日期时间,声明variable为
1 | DateTime? variable; |
作为Marc_s答案的补充,您可以使用更通用的扩展方法从sqldatareader获取值:
1 2 3 4 | public static T SafeGet<T>(this SqlDataReader reader, int col) { return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col); } |
您可以使用条件运算符:
1 | employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() :""; |
我们使用一系列静态方法从数据读取器中提取所有值。因此,在本例中,我们将调用
静态方法将包含检查空值的代码(请参阅本页上的其他答案)。
和/或使用三元运算符进行赋值:
1 2 | employee.FirstName = rdr.IsDBNull(indexFirstName))? String.Empty: rdr.GetString(indexFirstName); |
根据每个属性类型的需要替换默认值(当为空时)…
1 2 3 4 5 6 7 8 9 10 11 | 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(); } |
此方法依赖于indexfirstname,indexfirstname应该是基于零的列序号。
1 2 3 4 | if(!SqlReader.IsDBNull(indexFirstName)) { employee.FirstName = sqlreader.GetString(indexFirstName); } |
如果不知道列索引,但WAN不检查名称,则可以使用此扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } } |
使用这样的方法:
1 2 3 4 | if(sqlReader.HasColumn("FirstName")) { employee.FirstName = sqlreader["FirstName"]; } |
旧问题,但也许还有人需要答案
实际上,我就是这样解决这个问题的
INT:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 as"是空字符串
所以你可以像这样使用它
1 | int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?; |
和
1 | string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string; |
非常灵活,因此您可以插入任何查询来读取任何列,它将永远不会出错返回
通过影响getpsyched的答案,我创建了一个通用方法,该方法按列的名称检查列值。
1 2 3 4 5 | 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); } |
用途:
1 | var myVariable = SafeGet<string>(reader,"NameOfColumn") |
我正在使用下面列出的代码处理Excel工作表中的空单元格,该工作表将被读入数据表。
1 2 3 4 | if (!reader.IsDBNull(2)) { row["Oracle"] = (string)reader[2]; } |
convert敏感地处理dbnull。
1 | employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName)); |
以下是帮助程序类,其他人可以根据@marc_的答案在需要时使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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); } } |
你也可以查一下这个
1 2 | if(null !=x && x.HasRows) { ....} |