SQL数据读取器 - 处理空列值

Den*_*ail 276 c# sqldatareader

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

employee.FirstName = sqlreader.GetString(indexFirstName);
Run Code Online (Sandbox Code Playgroud)

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

mar*_*c_s 439

你需要检查IsDBNull:

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}
Run Code Online (Sandbox Code Playgroud)

这是检测和处理这种情况的唯一可靠方法.

我将这些内容包装到扩展方法中,如果列确实是这样,则往往返回默认值null:

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样调用它:

employee.FirstName = SqlReader.SafeGetString(indexFirstName);
Run Code Online (Sandbox Code Playgroud)

而且你永远不必再担心异常或null价值.

  • 如果有人需要**列名**而不是索引,你可以这样做:`int colIndex = reader.GetOrdinal(fieldname);`并轻松重载@ marc_s的`SafeGetString`函数. (58认同)
  • 不敢相信我在 2019 年在这里,在 VB 中也不少......不过谢谢,很有帮助 (4认同)

ste*_*ell 220

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

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);
Run Code Online (Sandbox Code Playgroud)

as运营商处理的铸造,包括支票的DBNull.

  • 如果有人将Age列从int更改为SQL bigint(c#long),则代码将通过返回0静默失败.来自ZXX的答案是更可靠的IMO. (6认同)
  • 请注意,在此处使用"as"可以隐藏索引错误.如果你不小心使用`sqlreader [indexAge]作为字符串?""`,你总会得到````.考虑一下你是否真的想要`(int?)sqlreader [indexAge] ?? 相反,defaultValue`,所以如果您的SQL更改,您将获得异常而不是错误的值.@ Stevo3000:default(int)是0,不是-1.@Chris:确保你正在使用你真正想要的那个. (5认同)
  • @Chris - 你应该能够用-1替换default(int). (4认同)

Gon*_*ing 30

对于字符串,您可以简单地转换对象版本(使用数组运算符访问)并使用空字符串为null:

employee.FirstName = (string)sqlreader[indexFirstName];
Run Code Online (Sandbox Code Playgroud)

要么

employee.FirstName = sqlreader[indexFirstName] as string;
Run Code Online (Sandbox Code Playgroud)

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

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();
Run Code Online (Sandbox Code Playgroud)

或null-coalescing运算符(??).

employee.Age = (sqlreader[indexAge] as int?) ?? 0;
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢这种接受答案的方法. (3认同)
  • 与您的第一个示例一样,显式转换不起作用。它抛出同样的错误 (3认同)
  • @GoneCoding:是的,它是一个可以为空的字符串,绝对是第一个在第二个工作时导致问题的情况。我想问题是由如何处理空值引起的。如在,它们不是 `null` 而是一个 DBNull 对象。两个语句之间的区别在于,如果第一个语句不是字符串,则第一个语句将失败,而第二个语句如果不是字符串,则仅返回 null。 (2认同)

ZXX*_*ZXX 21

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;
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它们:

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);
Run Code Online (Sandbox Code Playgroud)

  • 我发现System.Data.SqlTypes类型的显式运算符在尝试使用此代码的任何地方都会抛出错误... (5认同)

PJ3*_*PJ3 12

reader.IsDbNull(ColumnIndex) 尽可能多的答案说.

我想提一下,如果你使用列名,只比较类型可能会更舒服.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
Run Code Online (Sandbox Code Playgroud)


Mic*_*odd 11

一种方法是检查db nulls:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));
Run Code Online (Sandbox Code Playgroud)


小智 11

当使用列名在datareader中返回行时,我认为没有NULL列值.

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

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

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用reader [FieldName] == DBNull.Value来检查NULL (4认同)

Sum*_*ime 9

此解决方案不依赖于供应商,可与SQL,OleDB和MySQL Reader配合使用:

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);
}
Run Code Online (Sandbox Code Playgroud)


ale*_*lex 8

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

SELECT ISNULL(firstname, '') FROM people
Run Code Online (Sandbox Code Playgroud)

在这里,我用空字符串替换每个null.在这种情况下,您的代码不会出错.

  • 为什么采用静态的,单独的辅助方法?SqlDataReader上的扩展方法看起来不是更引人注目,更直观吗? (3认同)

Ces*_*Gon 6

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


Pro*_*hem 6

通过影响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);
}
Run Code Online (Sandbox Code Playgroud)

用法:

var myVariable = SafeGet<string>(reader, "NameOfColumn")
Run Code Online (Sandbox Code Playgroud)

  • 我不知道你是谁,但祝福你。该功能节省了三个多小时的工作时间。 (2认同)

Vij*_*jai 5

您可以编写一个Generic函数来检查Null,并在它为NULL时包含默认值.读取Datareader时调用此方法

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }
Run Code Online (Sandbox Code Playgroud)

在阅读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"]);
                         }
Run Code Online (Sandbox Code Playgroud)


小智 5

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

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }
Run Code Online (Sandbox Code Playgroud)