SQL Data Reader - 处理空列值

366

我正在使用 SQLdatareader 从数据库中构建 POCOs。代码运行良好,除非在数据库中遇到空值。例如,如果数据库中的 FirstName 列包含空值,将抛出异常。

employee.FirstName = sqlreader.GetString(indexFirstName);

如何在这种情况下处理空值?

2
我认为@Vijai创建的通用函数应该在这个线程中得到更多的认可。它对我来说非常有效,我不需要每次传递数据读取器对象。许多流行的答案都传递了数据读取器对象,这是不必要且沉重的。 - MSBI-Geek
30个回答

8

我的做法是用合适的值替换SELECT语句中的空值。

SELECT ISNULL(firstname, '') FROM people

在这里,我用空字符串替换了每个null。这样做可以避免你的代码出现错误。


如果可能的话,请使用此方法避免空值。否则,我喜欢Sonny Boy提供的辅助方法的答案。 - No Refunds No Returns
3
为什么要使用静态的独立辅助方法?在SqlDataReader上创建一个扩展方法不是更有吸引力和更直观吗? - marc_s
答案应该讲述处理它的C#方式,而不是使用SQL。在我的情况下,我无法控制SQL查询。 - Prateek Kumar Dalbehera

6
作为对marc_s答案的补充,您可以使用更通用的扩展方法从SqlDataReader中获取值:
public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }

我不会称呼这个方法为“SafeGet”,因为如果T是一个结构体,它将把null转换为T的非null默认值 - 不是真正的安全。也许可以称之为“GetValueOrDefault”。 - Rhys Jones
1
@RhysJones 在这种情况下,为什么要将T作为一个结构体呢?即使您这样做,我认为结构体中的非空默认值是预期的行为。 - getpsyched
@RhysJones 但我同意这种方法可能不安全,需要处理来自SqlDataReader的InvalidCastException等异常。 - getpsyched

6

在尝试读取之前,务必检查sqlreader.IsDBNull(indexFirstName)


4
如何创建辅助方法
字符串操作:
- 为 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,请声明变量为
DateTime? variable;

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

3
我认为你会想使用以下内容:

我认为您可能需要使用:

SqlReader.IsDBNull(indexFirstName)

2
您可以使用条件运算符:
employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";

和下面的一个答案一样,但是落后了8年! - beercohol

2
在C# 7.0中,我们可以执行以下操作:

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

如果是空值,它将保留空值。

2
这里有很多答案都提供了有用的信息(也有一些错误的信息),我想把它们整合在一起。
问题的简短答案是检查 DBNull - 几乎所有人都同意这一点 :)
与其使用一个帮助方法来读取每个 SQL 数据类型的可空值,使用通用方法可以让我们用更少的代码来解决这个问题。然而,你不能为可空值类型和引用类型使用单个通用方法,这在Nullable type as a generic parameter possible?C# generic type constraint for everything nullable中详细讨论过。
因此,在@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()。

2
我们使用一系列静态方法从数据阅读器中提取所有的值。因此,在这种情况下,我们将调用DBUtils.GetString(sqlreader(indexFirstName))。创建静态/共享方法的好处是你不必一遍又一遍地进行相同的检查... 静态方法将包含用于检查空值的代码(请参见本页其他答案)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接