关于c#:SQL数据读取器 – 处理空列值

SQL Data Reader - handling Null column values

我正在使用sqldatareader从数据库构建POCO。除非在数据库中遇到空值,否则代码可以工作。例如,如果数据库中的firstname列包含空值,则会引发异常。

1
employee.FirstName = sqlreader.GetString(indexFirstName);

在这种情况下,处理空值的最佳方法是什么?


您需要检查IsDBNull

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

这是检测和处理这种情况的唯一可靠方法。

我将这些东西包装成扩展方法,如果列确实是null,则倾向于返回默认值:

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

你再也不必担心异常或null值了。


您应该使用as运算符和??运算符组合作为默认值。值类型将需要被读取为可以为空并给定默认值。

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

as运算符处理强制转换,包括检查dbnull。


对于字符串,您可以简单地强制转换对象版本(使用数组运算符访问),并使用空字符串结束空值:

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;


IsDbNull(int)通常比使用GetSqlDateTime等方法慢得多,然后与DBNull.Value比较。尝试使用这些扩展方法来扩展SqlDataReader

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中返回行时,我认为没有空列值。

如果您执行datareader["columnName"].ToString();,它将始终为您提供一个可以是空字符串的值(如果需要比较,String.Empty)。

我会使用以下内容,不会担心太多:

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


reader.IsDbNull(ColumnIndex)的工作原理和许多答案一样。

我想说一下,如果您使用列名,只比较类型可能会更舒服。

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


此解决方案不依赖于供应商,可用于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

这里我用一个空字符串替换每个空值。在这种情况下,您的代码不会出错。


在你试图阅读之前,先检查一下sqlreader.IsDBNull(indexFirstName)


您可以编写一个通用函数来检查空值,并在其为空时包含默认值。读取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() :"";


我们使用一系列静态方法从数据读取器中提取所有值。因此,在本例中,我们将调用DBUtils.GetString(sqlreader(indexFirstName)),创建静态/共享方法的好处是您不必反复执行相同的检查…

静态方法将包含检查空值的代码(请参阅本页上的其他答案)。


和/或使用三元运算符进行赋值:

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)
{ ....}