340

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

employee.FirstName = sqlreader.GetString(indexFirstName);

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

4

30 回答 30

539

您需要检查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值了。

于 2009-11-20T17:25:52.843 回答
237

您应该将as运算符与运算符结合使用以??获得默认值。值类型将需要被读取为可为空并给出默认值。

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

操作员处理转换,as包括检查 DBNull。

于 2010-07-22T11:54:22.023 回答
33
employee.FirstName = sqlreader[indexFirstName] as string;

对于整数,如果您转换为可为空的 int,则可以使用 GetValueOrDefault()

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

或空合并运算符 ( ??)。

employee.Age = (sqlreader[indexAge] as int?) ?? 0;
于 2014-10-07T15:10:48.387 回答
27

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]);
于 2010-07-22T11:44:49.140 回答
16

reader.IsDbNull(ColumnIndex)正如许多答案所说的那样工作。

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

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
于 2016-07-28T08:20:18.647 回答
14

当使用列名在数据读取器中返回行时,我认为没有NULL列值。

如果你这样做datareader["columnName"].ToString();,它总是会给你一个可以是空字符串的值(String.Empty如果你需要比较)。

我会使用以下内容并且不会太担心:

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
于 2012-03-06T17:15:22.773 回答
13

一种方法是检查数据库空值:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));
于 2009-11-20T17:27:00.080 回答
12

此解决方案对供应商的依赖性较小,可与 SQL、OleDB 和 MySQL 阅读器一起使用:

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);
}
于 2012-10-16T08:18:34.237 回答
10

您可以编写一个通用函数来检查 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"]);
                         }
于 2016-09-20T00:13:34.970 回答
8

我倾向于做的是用适当的东西替换 SELECT 语句中的空值。

SELECT ISNULL(firstname, '') FROM people

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

于 2009-11-20T17:28:00.813 回答
7

通过影响getpsyched 的回答,我创建了一个通用方法,通过名称检查列值

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")
于 2018-12-24T11:57:45.730 回答
6

sqlreader.IsDBNull(indexFirstName)在尝试阅读之前检查。

于 2009-11-20T17:28:26.580 回答
5

作为对 marc_s 答案的补充,您可以使用更通用的扩展方法从 SqlDataReader 获取值:

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }
于 2018-09-06T12:42:27.563 回答
4

如何创建辅助方法

对于字符串

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

用法

MyStringConverter(read["indexStringValue"])

整数

 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;
于 2017-12-11T15:43:59.217 回答
4

这些都不是我想要的:

 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;
 }
于 2021-01-29T17:58:23.380 回答
3

我想你会想使用:

SqlReader.IsDBNull(indexFirstName)
于 2009-11-20T17:31:50.330 回答
2

我们使用一系列静态方法从数据读取器中提取所有值。所以在这种情况下,我们将调用DBUtils.GetString(sqlreader(indexFirstName)) 创建静态/共享方法的好处是您不必一遍又一遍地进行相同的检查......

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

于 2009-11-20T17:27:39.237 回答
2

您可以使用条件运算符:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
于 2017-08-22T12:10:02.937 回答
2

这里有很多有用信息(和一些错误信息)的答案,我想把它们放在一起。

这个问题的简短回答是检查 DBNull - 几乎每个人都同意这一点:)

通用方法允许我们用更少的代码来解决这个问题,而不是使用辅助方法来读取每个 SQL 数据类型的可空值。但是,对于可空值类型和引用类型,不能有一个泛型方法,这在 Nullable 类型中作为泛型参数进行了详细讨论?所有可为空的 C# 泛型类型约束

因此,根据@ZXX 和@getpsyched 的答案,我们最终得到了这两种获取可为空值的方法,并且我为非空值添加了第三种方法(它根据方法命名完成了集合)。

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

我通常使用列名,如果您使用列索引,请更改这些名称。基于这些方法名称,我可以判断我是否期望数据可以为空,这在查看很久以前编写的代码时非常有用。

尖端;

  • 数据库中没有可为空的列可避免此问题。如果您可以控制数据库,则默认情况下列应为非空,并且仅在必要时可为空。
  • 不要使用 C# 'as' 运算符转换数据库值,因为如果转换错误,它将默默地返回 null。
  • 使用默认值表达式会将数据库空值更改为 int、datetime、bit 等值类型的非空值。

最后,在测试所有 SQL Server 数据类型的上述方法时,我发现不能直接从 SqlDataReader 获取 char[],如果需要 char[],则必须获取字符串并使用 ToCharArray()。

于 2019-06-04T16:06:41.877 回答
1

和/或使用带赋值的三元运算符:

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

根据每个属性类型替换默认(当为 null 时)值...

于 2009-11-20T17:28:25.477 回答
1

我正在使用下面列出的代码来处理读取到数据表的 Excel 工作表中的空单元格。

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}
于 2012-11-02T13:43:28.083 回答
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();
        }
于 2013-05-31T06:25:11.510 回答
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"];
}
于 2016-02-12T12:47:28.617 回答
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;

非常灵活,因此您可以插入任何查询来读取任何列,并且永远不会返回错误

于 2018-02-11T22:11:22.853 回答
1

这是其他人可以根据@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);
        }
    }
于 2019-02-12T19:28:34.007 回答
1

在 c# 7.0 中,我们可以这样做:

var a = reader["ERateCode"] as string;
var b = reader["ERateLift"] as int?;
var c = reader["Id"] as int?;

所以如果是的话,它将保持空值。

于 2021-02-22T07:18:15.970 回答
0

转换处理 DbNull 明智。

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));
于 2019-03-28T00:11:31.097 回答
0

整洁的单线:

    while (dataReader.Read())
{
    employee.FirstName = (!dataReader.IsDBNull(dataReader.GetOrdinal("FirstName"))) ? dataReader["FirstName"].ToString() : "";
}
于 2020-10-15T07:58:23.443 回答
0

我尽力FieldDataTable. https://docs.microsoft.com/en-us/dotnet/api/system.data.datarowextensions.field

DBNull.Value如果您尝试将 a 转换为不可为空的类型,它将抛出。否则它将转换DBNull.Valuenull.

我还没有完全测试它。

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;
于 2021-04-21T15:56:00.347 回答
-3

你也可以检查一下

if(null !=x && x.HasRows)
{ ....}
于 2011-04-20T08:57:49.930 回答