将DataReader的值转换为可空变量

11

我尝试运行以下代码,但出现了强制类型转换错误。如何重写我的代码以达到相同的效果?

boolResult= (bool?)dataReader["BOOL_FLAG"] ?? true;
intResult= (int?)dataReader["INT_VALUE"] ?? 0;

感谢


你知道 dataReader["BOOL_FLAG"] 和 dataReader["INT_VALUE"] 中实际包含的值是什么吗? - Jonas
9个回答

18
使用数据读取器上的 "IsDbNull" 方法...例如:
使用数据读取器上的 "IsDbNull" 方法来检查是否为空,例如:
bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? null : (bool)dataReader["Bool_Flag"]

编辑

您需要执行类似于以下操作:

bool? nullBoolean = null;

然后您会拥有

bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? nullBoolean : (bool)dataReader["Bool_Flag"]

你说得对,我更新了我的答案... 我忘记它无法判断null是一个字符串、布尔值、MyCustomObject还是其他什么。 - taylonr
2
你可以简单地执行 ? (bool?)null : (bool)dataReader["Bool_Flag"] - Winston Smith
1
如果被测试的列是一个日期时间字段怎么办?IsDbNull方法需要一个整数。算了,我想我可以在方法调用中作为参数进行序数查找。 - Iofacture

12
考虑将其放入函数中。
以下是我过去使用过的代码(您可以在.NET 4中将其作为扩展方法):
public static T GetValueOrDefault<T>(SqlDataReader dataReader, System.Enum columnIndex)
{
    int index = Convert.ToInt32(columnIndex);

    return !dataReader.IsDBNull(index) ? (T)dataReader.GetValue(index) : default(T);
}

编辑

作为一个扩展(未经测试,但您会明白),并使用列名而不是索引:

public static T GetValueOrDefault<T>(this SqlDataReader dataReader, string columnName)
{

    return !dataReader.IsDBNull(dataReader[columnName]) ? (T)dataReader.GetValue(dataReader[columnName]) : default(T);
}

用法:

bool? flag = dataReader.GetValueOrDefault("BOOL_COLUMN");

6
我喜欢第二种扩展方法。但是,由于IsDBNull()只接受一个整数参数,所以你需要像这样调用:dataReader.IsDBNull(dataReader.GetOrdinal(columnName)) - Ishmael Smyrnow
使用方式如下:bool? flag = dataReader.GetValueOrDefault<bool?>("BOOL_COLUMN"); - René

6

这里有一个可能会有帮助的答案: https://dev59.com/rXI-5IYBdhLWcg3wm5lG#3308515

你可以使用"as"关键字。请注意评论中提到的警告。

nullableBoolResult = dataReader["BOOL_FLAG"] as bool?;

或者,如果您没有使用可空类型,就像您最初的帖子中所述:
boolResult = (dataReader["BOOL_FLAG"] as bool?) ?? 0;

我尝试将dataReader["long_col"]作为可空长整型long?,但它在所有情况下都将值设置为null。 - bpeikes

2
bool? boolResult = null;
int? intResult = null;

if (dataReader.IsDBNull(reader.GetOrdinal("BOOL_FLAG")) == false)
{
  boolResult  = dataReader.GetBoolean(reader.GetOrdinal("BOOL_FLAG"));
}
else
{
  boolResult = true;
}

if (dataReader.IsDBNull(reader.GetOrdinal("INT_VALUE")) == false)
{
   intResult= dataReader.GetInt32(reader.GetOrdinal("INT_VALUE"));
}
else
{
   intResult = 0;
}

2
使用扩展方法:
public static T GetValueOrDefault <T> (this SqlDataReader reader, string column) {
    var isDbNull = reader[column] == DBNull.Value;
    return !isDbNull ? (T) reader[column] : default (T);
}

1

我确定我在互联网上找到了这个灵感,但现在似乎找不到原始来源了。无论如何,下面您会发现一个实用类,它允许在DataReader上定义扩展方法,就像这样:

public static class DataReaderExtensions
{
    public static TResult Get<TResult>(this IDataReader reader, string name)
    {
        return reader.Get<TResult>(reader.GetOrdinal(name));
    }

    public static TResult Get<TResult>(this IDataReader reader, int c)
    {
        return ConvertTo<TResult>.From(reader[c]);
    }
 }

使用方法:

  reader.Get<bool?>("columnname")

或者

 reader.Get<int?>(5)

这是启用实用类:

public static class ConvertTo<T>
{
    // 'Factory method delegate', set in the static constructor
    public static readonly Func<object, T> From;

    static ConvertTo()
    {
        From = Create(typeof(T));
    }

    private static Func<object, T> Create(Type type)
    {
        if (!type.IsValueType) { return ConvertRefType; }
        if (type.IsNullableType())
        {
            return (Func<object, T>)Delegate.CreateDelegate(typeof(Func<object, T>), typeof(ConvertTo<T>).GetMethod("ConvertNullableValueType", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new[] { type.GetGenericArguments()[0] }));
        }
        return ConvertValueType;
    }

    // ReSharper disable UnusedMember.Local
    // (used via reflection!)
    private static TElem? ConvertNullableValueType<TElem>(object value) where TElem : struct
    {
        if (DBNull.Value == value) { return null; }
        return (TElem)value;
    }
    // ReSharper restore UnusedMember.Local


    private static T ConvertRefType(object value)
    {
        if (DBNull.Value != value) { return (T)value; }
        return default(T);
    }

    private static T ConvertValueType(object value)
    {
        if (DBNull.Value == value)
        {
            throw new NullReferenceException("Value is DbNull");
        }
        return (T)value;
    }
}

编辑:使用如下定义的IsNullableType()扩展方法:

    public static bool IsNullableType(this Type type)
    {
        return 
            (type.IsGenericType && !type.IsGenericTypeDefinition) && 
            (typeof (Nullable<>) == type.GetGenericTypeDefinition());
    }

太棒了。谢谢你。 - Steven Wilber

1
这是我对扩展方法的尝试。列名称语义,遇到 null 时回退到 default(T)
public static class DbExtensions
{
    public static T ReadAs<T>(this IDataReader reader, string col)
    {
        object val = reader[col];
        if (val is DBNull)
        {
            // Use the default if the column is null
            return default(T);
        }
        return (T)val;
    }
}

这是一个示例用法。请记住,尽管 string 是引用类型,但仍无法从 DBNull 转换为 null。对于 int? 也是如此。
public Facility Bind(IDataReader reader)
{
    var x = new Facility();
    x.ID = reader.ReadAs<Guid>("ID");
    x.Name = reader.ReadAs<string>("Name");
    x.Capacity = reader.ReadAs<int?>("Capacity");
    x.Description = reader.ReadAs<string>("Description");
    x.Address = reader.ReadAs<string>("Address");
    return x;
}

0

请记住,DBNull和null不是同一回事,因此您不能从一个转换为另一个。正如其他帖子所说,您可以使用IsDBNull()方法来检查DBNull。


-2

尝试使用this版本。它执行一些基本的转换,并管理默认值。


1
虽然这理论上回答了问题,但最好在此处包含答案的基本部分,并提供参考链接。 - Shawn Chin

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