从IDataReader获取值的空安全方式

12
(LocalVariable)ABC.string(Name) = (IDataReader)dataReader.GetString(0);

这个name值是从数据库中获取的。

这里的情况是,如果读取到的namenull,则会抛出异常?

我在这里手动进行一些if条件判断。我不想手动编写检查所有变量的条件。

我现在正在做这样的事情...

String abc = dataReader.GetValue(0);
if (abc == null)
   //assigning null
else
   //assigning abc value

是否有类似的东西,可以为这个编写扩展方法?


我建议使用您想要的方法:如果列为NULL,则dataReader.GetString(0)返回""(空字符串)。 - Ian Boyd
5个回答

25
以下是一些扩展方法,将完美地解决从数据读取器中检索强类型值的所有问题。如果该值为 DbNull,则将返回其类型的默认值。对于是类的 string 类型,将返回 null。如果字段是 int,则将返回 0。此外,如果您期望得到一个 int?,例如从可空 int 字段中获取,那么将返回 null。
针对 Kumar 案例的具体用法:
string abc = datareader.GetValueOrDefault<string>(0);

一般用法

var name = GetValueOrDefault<string>(reader, "Name");

或者

var name = reader.GetValueOrDefault<string>("Name");

或者

var name = reader.GetValueOrDefault<string>(0);

扩展

public static class NullSafeGetter
{
   public static T GetValueOrDefault<T>(this IDataRecord row, string fieldName)
   {
       int ordinal = row.GetOrdinal(fieldName);
       return row.GetValueOrDefault<T>(ordinal);
   }

   public static T GetValueOrDefault<T>(this IDataRecord row, int ordinal)
   {
       return (T)(row.IsDBNull(ordinal) ? default(T) : row.GetValue(ordinal));
   }
}

来自http://skysanders.net/subtext/archive/2010/03/02/generic-nullsafe-idatarecord-field-getter.aspx

这个链接提供了一个泛型方法,它可以在不抛出异常的情况下获取IDataRecord的字段值。


1
如果值为DBNull且我们期望int,那么仅返回0不好吧?在这种情况下,我们不应该抛出异常吗,因为显然有些问题 - 我们应该期望int?或者查询产生了错误类型的值?在没有实际值的地方放置0可能在某些特定情况下是可以的,但是在一些常用库中使用它可能会导致非常微妙的错误,我认为。 - poke
能够传递默认值作为参数是很方便的:public static T GetValueOrDefault<T>(this IDataRecord row, int ordinal, T defaultValue = default(T)) - mistika
对于 int 类型的情况,如果你想让 T 成为 int?,这样返回值就可以为 null。 - Scott Chamberlain
@Scott 不是这样的,我不希望返回值为null,当我期望int时,我希望该方法返回int而不是int?并且我希望它与数据库中的完全一样。此外,当IDataReader要求一个int,但读者拥有的基础值是DBNull.Value时,那显然是类型不匹配,我们不应该返回默认的int值,零,也不应该返回其他任何值,我们应该抛出异常。 - poke
太好了!感谢这个优雅的解决方案。 - Scott Fraley

4
类似于 @sky-sanders 的回答,但是在转换时不那么严格。
public static T Get<T>(this IDataRecord row, string fieldName)
{
    int ordinal = row.GetOrdinal(fieldName);
    return row.Get<T>(ordinal);
}

public static T Get<T>(this IDataRecord row, int ordinal)
{
    var value = row.IsDBNull(ordinal) ? default(T) : row.GetValue(ordinal);
    return (T)Convert.ChangeType(value, typeof(T));
}

2

我的解决方案是:

private static T GetValue<T>(object o) {
    if (typeof(DBNull) != o.GetType()) {
        return (T) o;
    }
    return default(T);
}

Status = GetValue<string>(currentDataRow["status"]) 时:


4
可以使用 "if (o is DBNull)",而不是使用 typeof() 和 GetType()。在我看来,这样更易读。 - iheartcsharp
很有可能 o == DBNull.Valueo is DBNull 更快,而且肯定比 typeof(DBNull) != o.GetType() 更快。 - nawfal

2

结合最佳解决方案和建议,这里是一个支持使用可选默认值参数的GetValue<T>GetValueOrDefault<T>C# 6箭头表达式版本。

public static class DataRecordExtensions {
    /// <summary>
    /// Generically extracts a field value by name from any IDataRecord as specified type. Will throw if DNE.
    /// </summary>
    public static T GetValue<T>(this IDataRecord row, string fieldName)
        => row.GetValue<T>(row.GetOrdinal(fieldName));

    /// <summary>
    /// Generically extracts a field value by ordinal from any IDataRecord as specified type. Will throw if DNE.
    /// </summary>
    public static T GetValue<T>(this IDataRecord row, int ordinal)
        => (T)row.GetValue(ordinal);

    /// <summary>
    /// Generically extracts a field value by name from any IDataRecord as specified type. Will return default generic types value if DNE.
    /// </summary>
    public static T GetValueOrDefault<T>(this IDataRecord row, string fieldName, T defaultValue = default(T))
        => row.GetValueOrDefault<T>(row.GetOrdinal(fieldName), defaultValue);

    /// <summary>
    /// Generically extracts a field value by ordinal from any IDataRecord as specified type. Will return default generic types value if DNE.
    /// </summary>
    public static T GetValueOrDefault<T>(this IDataRecord row, int ordinal, T defaultValue = default(T))
        => (T)(row.IsDBNull(ordinal) ? defaultValue : row.GetValue(ordinal));
}

0
我会使用类似这样的代码:
string abc = (IDataReader)datareader.GetValue(0) ?? "Default";

?? 运算符被称为 null 合并运算符,用于为可空值类型和引用类型定义默认值。如果左操作数不为 null,则返回左操作数;否则返回右操作数。http://msdn.microsoft.com/en-us/library/ms173224.aspx - Powerlord
2
你测试过这个代码吗?因为 DbNull != null - Sky Sanders
@Sky:没有要求检查 DbNull - 这段代码只检查 null。如果我写了检查 DbNull 的代码,那么我将改变代码的行为,而我不想这样做。 - Jaxidian
你是否期望一个IDataReader会包含null? 而要求使用扩展方法。但我的问题是:你测试了你提供的代码吗?对我来说它似乎有点不靠谱。 - Sky Sanders
@Sky:不,它甚至无法编译,因为@kumar发布的代码解释了他当前在做什么,但是这个代码无法编译。他和我都在有效地说“string foo = (object)bar;”,这是无法编译的。此外,我甚至没有声称它编译过。这里的想法是让我传达一个思想,而不是替他做所有的工作。我尝试使用他用来表达问题的同样的沟通方式来传达这个想法——我认为这样会更容易实现。而且我所做的并不会改变代码的行为。为什么要那么憎恨呢? - Jaxidian
6
没有恶意。我只是指出你的答案既不了解基本问题,也没有有效的解决方案。此外,测试 GetValue 是否为空值是很愚蠢的。如果要提供一个明确或隐含地体现对该问题理解的有效答案比你想要做的工作还要多,那么最好不要回答。原帖作者对问题领域并没有清晰的了解,回答者的责任是弥补这一点,而不是用类似的垃圾代码使问题更加复杂。和平。 - Sky Sanders

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