如果值为null,我该如何将一个值返回给SqlDataReader?

6

我目前正在使用一个 SQL 数据读取器 (在 vb.net 中) 通过存储过程从 SQL Server 2008 数据库中提取文章对象。其中包括下面显示的两个属性:

theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness"))))
theArticle.Relevance = ((myReader.GetInt32(myReader.GetOrdinal("Relevance"))))

我的问题是真实性和相关性可能会返回空值,这会导致函数失败。

我想我明白原因了。我正在请求一个整数值(getin32),因为返回了null,所以它失败了。

我该如何适应数据库中的null值,使其不失败?

7个回答

20

您可以使用.IsDBNull()来检查给定的序数位置是否为空,然后执行一些操作 - 例如将值设置为-1或其他值:

您可以使用.IsDBNull()方法来检查特定序号位置是否为null,如果是,则可以采取某些措施 - 例如将值设为-1或其他值。

int myOrdinal = myReader.GetOrdinal("Truthfullness");

if(myReader.IsDBNull(myOrdinal))
{
  theArticle.Truthfulness = -1;
}
else
{
  theArticle.Truthfulness = myReader.GetInt32(myOrdinal);
}

正如Mike Hofer在他的回答中指出的那样,您还可以将所有这些逻辑封装到扩展方法中:

public static class SqlDataReaderExtensions 
{
    public static int SafeGetInt32(this SqlDataReader reader, 
                                   string columnName, int defaultValue) 
    {
        int ordinal = reader.GetOrdinal(columnName);

        if(!reader.IsDbNull(ordinal))
        {
           return reader.GetInt32(ordinal);
        } 
        else
        {
           return defaultValue;
        }
    }
}

然后只需使用那个"SafeGetInt32"方法即可:

  theArticle.Truthfulness = myReader.SafeGetInt32("Truthfullness", -1);

Marc


或者捕获异常并处理它 - mmmmmm
2
是的,你也可以这样做 - 但避免异常比捕获和处理异常更好(通常情况下)。 - marc_s
1
@marc_s:同意你对我的回答的评论。我已将其删除。感谢你澄清了这一点。你的评论是:“我认为这不会起作用,因为如果数据库列为空,.GetInt32()调用将失败并引发异常——你将无法获得一个空值,然后将其馈送到“??”运算符中...” - MRG
现在你可能会返回一个可空的整数:https://dev59.com/HknSa4cB1Zd3GeqPPqFO#53391141 - Daniel Hilgarth

2
你是否查看了SqlDataReader.IsDBNull方法?可能类似于以下内容:
if(myReader.IsDBNull(myReader.GetOrdinal("Truthfulness"))
theArticle.Truthfulness = string.Empty;
else
theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness"))))

如果你正在处理 int32 属性,那么 string.Empty 并不会有太大作用...... - marc_s

1

这个通用版本可能会有用:

    private T ValueOrDefault<T>(System.Data.IDataReader rdr, string columnName)
    {
        T vod = default(T);
        try
        {
            int idx = rdr.GetOrdinal(columnName);
            if (!rdr.IsDBNull(idx))
                return (T)rdr[idx];
        }
        catch (IndexOutOfRangeException) { }

        return vod;
    }

是否可以扩展以捕获 InvalidCastException,或者使用 Convert.ChangeType 而不是强制转换?


除了 IndexOutOfRangeException 的 catch 语句之外,我喜欢这个选项。我认为你会希望尽快知道模式结构是否发生了变化,而不是将默认值推送到系统的其他部分。 - Blake Blackwell

1

你知道的,在Oracle中我经常处理这个问题。为了简化操作,我编写了一组扩展方法来清理代码:

using System.Data.OracleClient;
public static class OracleDataReaderExtensions 
{
    public static int GetInt32(this OracleDataReader reader, string columnName, int defaultValue) 
    {
        return reader.GetInt32(reader.GetOrdinal(columnName)) != DbNull.Value ? 
               reader.GetInt32(reader.GetOrdinal(columnName)) : 
               defaultValue;
    }
}

为每个要返回的类型创建单独的重载。我主要使用字符串、整数、日期和十进制数。记住 YAGNI(你不需要处理读取器支持的每种类型,只需要处理实际使用的类型)。
像这样针对 SQL Server 的扩展类非常容易编写,并且将极大地简化您的工作。相信我吧。我会骗你吗? :)

1
我认为这不会起作用,因为如果数据库中的列为空,则调用GetInt32()将导致异常。您无法将该GetInt32()调用与DBNull.Value进行比较并对其做出反应... - marc_s
它能够工作的原因是因为我们使用了短路 ?: 运算符。如果它不是 null,我们就返回它。否则,我们返回默认值。 - Mike Hofer
实际上,至少在使用SQL Server时,它是*不起作用的 - 如果底层数据库列包含NULL,则GetInt32()将抛出异常,并且您无法将GetInt32()调用与DBNull.Value进行比较 - VS2008会发出警告甚至无法编译它。 - marc_s
但我绝对喜欢将其封装在扩展方法中的想法 - 这样会让生活变得更加轻松! - marc_s
我尝试使用你的方法,将reader.GetInt32()的输出与SQL Server中的DBNull.Value进行比较:但是我得到了这个错误消息:运算符'!='不能应用于类型为'int'和'System.DBNull'的操作数。 - marc_s
显示剩余2条评论

0

现在,如果数据库返回null,您可能希望使用Nullable<int>

public static class Extensions
{
    public static int? GetNullableInt32(this SqlDataReader reader, int ordinal)
    {
        if (reader.IsDBNull(ordinal))
            return null;

        return reader.GetInt32(ordinal);
    }

    public static long? GetNullableInt64(this SqlDataReader reader, int ordinal)
    {
        if (reader.IsDBNull(ordinal))
            return null;

        return reader.GetInt64(ordinal);
    }
}

0

IsDbNull(int)通常比使用GetSqlInt32等方法并将其与DBNull.Value进行比较或使用其自己的.IsNull要慢得多。

    public static int Int32(this SqlDataReader r, int ord)
    {
        var t = r.GetSqlInt32(ord);
        return t.IsNull ? default(int) : t.Value;
    }

尝试了几个模板解决方案,但迄今为止都没有成功。问题在于所有的 Sql 类型(这里是 SqlInt32)类型实际上都是结构体,虽然它们都有 .Value 属性,但 C# 没有真正的模板来处理它们。此外,它们还有自己的 INullable 接口,该接口只有 .IsNull 属性,与 Nyllable<> 不兼容。
我怀疑需要将 Sql 类型作为 C# 模板的完整集合或者将 IConvertible 添加到它们中,以便能够只使用一个或两个模板方法。
如果有人有一些功能技巧的想法,请说出来 :-)

0

以下是我们在SQLServer上使用的代码,它十分有效:

...

  Dim X as Object = pbDr("TotAmt")  'dr is dim'ed as a DataReader

...

  Public Function pbDr(ByVal drName As String) As Object

    Dim SQLError As SqlClient.SqlException

    Dim IsNull As Boolean

    Dim Ordinal, DispNbr As Integer

    Try
      Ordinal = dr.GetOrdinal(drName)
      IsNull = dr.IsDBNull(Ordinal)
      If IsNull Then
        Dim Dbtype As String = dr.GetFieldType(Ordinal).ToString
        If Dbtype = "System.String" Then
          Return ""
        ElseIf Dbtype = "System.Int32" _
         OrElse Dbtype = "System.Double" _
         OrElse Dbtype = "System.Decimal" _
         OrElse Dbtype = "System.Int16" Then
          Return 0
        Else
          MsgBox("Print This Screen And Send To Support" _
           & "pbdr-Object = " & Dbtype, MsgBoxStyle.Critical)
          Return ""
        End If
      Else
        Return dr(Ordinal)
      End If

    Catch sqlerror
      Call DispSQLError(SQLError, "pbDr")
      pbDr = ""
    End Try

  End Function

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