如何在使用Datareader时处理空值

7

假设我有这个类:

class myclass
{
    public int Field1{ get; set; }
    public int? Field2 { get; set; } //Note Field2 is nullable
 }

我正在尝试使用来自数据库的数据填充一个通用列表。由于GetSqlInt32实现了INullable接口,所以我认为下面的代码应该可以工作。但事实并非如此。如果Field2为空,它会生成一个错误。

List<myclass> mylist=new List<myclass>();

int Field1_Ordinal = rdr.GetOrdinal("Field1");
int Field2_Ordinal = rdr.GetOrdinal("Field2");

SqlDataReader rdr = cmd.ExecuteReader(); //Execute a stored procedure to retrieve data from the database

while (rdr.Read())
 {
   mylist.Add(new myclass
   {
      Field1 = rdr.GetSqlInt32(Field1_Ordinal).Value,
      Field2 = rdr.GetSqlInt32(Field2_Ordinal).Value  //Error if field2 is null
   });
 }

有什么想法,为什么它不工作?
7个回答

24

我认为您需要进行这样的转换(使用扩展方法方便进行):

public static int? ToNullableInt32(this SqlInt32 value)
{
    return value.IsNull ? (int?) null : value.Value;
}

接下来:

Field2 = rdr.GetSqlInt32(Field2_Ordinal).ToNullableInt32()

(关于其他答案的评论:没有必要提及 DbNull,因为 SqlInt32 已经可以表示空值。在使用 Value 之前,您只需要检测是否为空即可。)


谢谢您的回复,但为什么不使用扩展方法来实现类似以下代码的效果呢:Field2 = (rdr.IsDBNull(Field2_Ordinal) ? (int?)null : rdr.GetSqlInt32(Field2_Ordinal).Value) - Anthony
@Anthony:基本上是简单易懂。你认为哪个版本更容易阅读? :) (是的,这意味着有额外的扩展方法,但你只需要那个一次,无论你有多少可空整数字段。) - Jon Skeet
reader.GetFieldValue<int?> 应该能够处理空值吧? - Farinha
@Farinha:老实说,我不知道它是否会。DbReader.GetFieldValue的文档没有提到这一点... - Jon Skeet
你说得对,DbReader.GetFieldValue 的文档甚至指定了它支持的类型,而 int? 不是其中之一。按照建议创建了一个扩展方法。 - Farinha
显示剩余2条评论

6

请看这个解决方案,不是我写的:

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

原始提议在这里:

SQL Data Reader - 处理 Null 列值


该问题涉及如何处理 SQL 数据阅读器(SQL Data Reader)中的空值列。

4
这里有一个减少疼痛的变体主题。如果有人知道如何将Val和Ref合并为一个模板函数,请随意发布。您必须明确声明类型(C#编译器无法打扰:-),但是这样做:
var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

仍能让我的手指感到愉悦 :-)

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

1

我认为这是因为返回的值是 DBNull.Value,而不是 null

相反,您可以使用 IsDbNull() 方法在读取字段之前检查它是否为空。


1
返回的值是 SqlInt32,而不是 DBNull.Value。 - Jon Skeet

1

你必须在读取器上使用特殊的方法来检测值是否为 null

mylist.Add(new myclass   
{      
     Field1 = rdr.IsDbNull(Field1_Ordinal)? 0: 
               rdr.GetSqlInt32(Field1_Ordinal).Value,      
     Field2 = rdr.IsDbNull(Field2_Ordinal)? 0:  // whatever default value you wish...
               rdr.GetSqlInt32(Field2_Ordinal).Value  // No error now
});

在我的情况下: Field2 = rdr.IsDbNull(Field2_Ordinal)? (int?) null: rdr.GetSqlInt32(Field2_Ordinal).Value - Anthony

1

我正在尝试导出一个包含39个字段的Access数据库,其中许多字段具有NULL值。我无法使扩展方法起作用,因此编写了以下函数:

private string ChkDbStr(object inObj)
{
  if (inObj == null)
  { return ""; }
  else
  { return inObj.ToString(); }
}

当我读取可能包含NULL的每个字段时,我编写的字段读取代码如下: ChkDbStr(DbReader.GetValue(1))


0

DbNull.Value != null

因此,您需要使用 ? : 表达式或 if 块将数据库 null 转换为 c# null 或反之亦然。


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