我正在使用SQLdatareader从数据库构建POCO。代码工作,除非它在数据库中遇到空值。例如,如果数据库中的FirstName列包含空值,则抛出异常。
employee.FirstName = sqlreader.GetString(indexFirstName);
在这种情况下处理空值的最佳方法是什么?
答案 0 :(得分:411)
您需要检查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
值。
答案 1 :(得分:217)
您应该将as
运算符与??
运算符结合使用以获取默认值。值类型需要被读取为可空并且给定默认值。
employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);
as
运算符处理转换,包括检查DBNull。
答案 2 :(得分:28)
对于一个字符串,您可以简单地转换对象版本(使用数组运算符访问)并使用空字符串为null:
employee.FirstName = (string)sqlreader[indexFirstName];
或
employee.FirstName = sqlreader[indexFirstName] as string;
对于整数,如果转换为可以为null的int,则可以使用GetValueOrDefault()
employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();
或null-coalescing运算符(??
)。
employee.Age = (sqlreader[indexAge] as int?) ?? 0;
答案 3 :(得分:19)
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]);
答案 4 :(得分:11)
一种方法是检查db nulls:
employee.FirstName = (sqlreader.IsDBNull(indexFirstName)
? ""
: sqlreader.GetString(indexFirstName));
答案 5 :(得分:11)
当使用列名在datareader中返回行时,我认为没有 NULL 列值。
如果您执行datareader["columnName"].ToString();
,它将始终为您提供一个可以为空字符串的值(String.Empty
,如果您需要进行比较)。
我会使用以下内容并且不会太担心:
employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
答案 6 :(得分:10)
reader.IsDbNull(ColumnIndex)
可以解答很多答案。
我想提一下你是否使用列名,只是比较类型可能会更舒服。
if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
答案 7 :(得分:9)
此解决方案与供应商相关性较低,可与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);
}
答案 8 :(得分:7)
我倾向于用适当的东西替换SELECT语句中的空值。
SELECT ISNULL(firstname, '') FROM people
这里我用空字符串替换每个null。在这种情况下,您的代码不会出错。
答案 9 :(得分:6)
在尝试阅读之前检查sqlreader.IsDBNull(indexFirstName)
。
答案 10 :(得分:5)
您可以编写一个通用函数来检查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"]);
}
答案 11 :(得分:3)
我想你会想要使用:
SqlReader.IsDBNull(indexFirstName)
答案 12 :(得分:3)
作为marc_s答案的补充,您可以使用更通用的扩展方法从SqlDataReader获取值:
public static T SafeGet<T>(this SqlDataReader reader, int col)
{
return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
}
答案 13 :(得分:3)
如何创建辅助方法
For String
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 :(得分:2)
我们使用一系列静态方法从数据读取器中提取所有值。所以在这种情况下我们会调用DBUtils.GetString(sqlreader(indexFirstName))
创建静态/共享方法的好处是你不必一遍又一遍地做同样的检查......
静态方法将包含检查空值的代码(请参阅本页的其他答案)。
答案 15 :(得分:2)
您可以使用条件运算符:
employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
答案 16 :(得分:1)
在 c# 7.0 中我们可以这样做:
var a = reader["ERateCode"] as string;
var b = reader["ERateLift"] as int?;
var c = reader["Id"] as int?;
所以它会保留空值。
答案 17 :(得分:1)
这些都不是我想要的:
public static T GetFieldValueOrDefault<T>(this SqlDataReader reader, string name)
{
int index = reader.GetOrdinal(name);
T value = reader.IsDBNull(index) ? default(T) : reader.GetFieldValue<T>(index);
return value;
}
答案 18 :(得分:1)
受getpsyched's answer的影响,我创建了一个通用方法,该方法按列名检查列值
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")
答案 19 :(得分:1)
此方法依赖于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"];
}
答案 20 :(得分:1)
老问题,但也许有人仍需要答案
在实际中,我解决了这个问题
对于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;
非常灵活,因此您可以插入任何查询来读取任何列,它将永远不会返回错误
答案 21 :(得分:1)
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();
}
答案 22 :(得分:1)
我使用下面列出的代码来处理读入数据表的Excel工作表中的空单元格。
if (!reader.IsDBNull(2))
{
row["Oracle"] = (string)reader[2];
}
答案 23 :(得分:1)
和/或使用带有赋值的三元运算符:
employee.FirstName = rdr.IsDBNull(indexFirstName))?
String.Empty: rdr.GetString(indexFirstName);
替换每个属性类型的默认值(当为null时)...
答案 24 :(得分:0)
以下是帮助类,其他人可以根据@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);
}
}
答案 25 :(得分:0)
转换明智地处理DbNull。
employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));
答案 26 :(得分:0)
这里有很多答案,其中包含有用的信息(以及一些错误的信息),我希望将它们结合在一起。
这个问题的简短答案是检查DBNull-几乎每个人都同意这一点:)
泛型方法无需使用辅助方法来读取每种SQL数据类型的可为空的值,而是使我们可以用更少的代码来解决这个问题。但是,对于可为空的值类型和引用类型,您都不能使用单一的通用方法,有关详细信息,请参见 Nullable type as a generic parameter possible?和C# generic type constraint for everything nullable。
因此,从@ZXX和@getpsyched的答案开始,我们最终得到了两种用于获取可为空的值的方法,并且我为非空值添加了第三个方法(它基于方法命名来完成设置)
nil
我通常使用列名,如果使用列索引,请更改它们。基于这些方法名称,我可以判断我是否期望数据为空,这在查看很久以前编写的代码时非常有用。
提示;
最后,在测试所有SQL Server数据类型的上述方法时,我发现您无法直接从SqlDataReader获取char [],如果要char [],则必须获取字符串并使用ToCharArray( )。
答案 27 :(得分:0)
整齐的一线:
while (dataReader.Read())
{
employee.FirstName = (!dataReader.IsDBNull(dataReader.GetOrdinal("FirstName"))) ? dataReader["FirstName"].ToString() : "";
}
答案 28 :(得分:0)
我已尽力重新实现 Field
中的 DataTable
方法。 https://docs.microsoft.com/en-us/dotnet/api/system.data.datarowextensions.field
如果您尝试将 DBNull.Value
转换为不可为 null 的类型,它会抛出异常。否则,它会将 DBNull.Value
转换为 null
。
我还没有完全测试过。
public static T Field<T>(this SqlDataReader sqlDataReader, string columnName)
{
int columnIndex = sqlDataReader.GetOrdinal(columnName);
if (sqlDataReader.IsDBNull(columnIndex))
{
if (default(T) != null)
{
throw new InvalidCastException("Cannot convert DBNULL value to type " + typeof(T).Name);
}
else
{
return default(T);
}
}
else
{
return sqlDataReader.GetFieldValue<T>(columnIndex);
}
}
用法:
string fname = sqlDataReader.Field<string>("FirstName");
int? age = sqlDataReader.Field<int?>("Age");
int yearsOfExperience = sqlDataReader.Field<int?>("YearsEx") ?? 0;
答案 29 :(得分:-2)
你也可以检查这个
if(null !=x && x.HasRows)
{ ....}