我使用SQLdatareader从数据库构建poco。除非在数据库中遇到空值,否则代码可以正常工作。例如,如果数据库中的FirstName列包含空值,则抛出异常。

employee.FirstName = sqlreader.GetString(indexFirstName);

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


当前回答

我尽了最大努力从DataTable中重新实现Field方法。https://learn.microsoft.com/en-us/dotnet/api/system.data.datarowextensions.field

如果您尝试转换DBNull,它将抛出。值转换为非空类型。否则它将转换为DBNull。值为null。

我还没有完全测试过。

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;

其他回答

我尽了最大努力从DataTable中重新实现Field方法。https://learn.microsoft.com/en-us/dotnet/api/system.data.datarowextensions.field

如果您尝试转换DBNull,它将抛出。值转换为非空类型。否则它将转换为DBNull。值为null。

我还没有完全测试过。

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;

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

你应该使用as运算符和?? ?操作符用于默认值。值类型需要被读取为空,并给出默认值。

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

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

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

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

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

我会使用以下方法,不会太担心:

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