【问题标题】:How do I return a value to a sqldatareader if value is null?如果值为空,如何将值返回给 sqldatareader?
【发布时间】:2009-10-03 10:33:52
【问题描述】:

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

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

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

我想我明白为什么了。我要求一个整数值(getin32),因为返回 null 它失败。

如何容纳数据库中的空值,使其不会倒下?

【问题讨论】:

    标签: sql sql-server vb.net sql-server-2008


    【解决方案1】:

    您可以使用.IsDBNull() 检查给定的序数位置是否为空,然后执行某些操作 - 例如将您的值设置为 -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_s :同意您对我的回答的评论。删除它。谢谢你说清楚。您的评论是“我不认为这会起作用,因为如果 db 列为 NULL,则 .GetInt32() 调用将失败并出现异常 - 您将无法返回 NULL 值,然后您可以将其输入“ ??" 运算符...."
    • 现在您可能会返回一个可为空的 int:stackoverflow.com/a/53391141/572644
    【解决方案2】:

    你检查了吗,SqlDataReader.IsDBNull 方法?大概是这样的:

    if(myReader.IsDBNull(myReader.GetOrdinal("Truthfulness"))
    theArticle.Truthfulness = string.Empty;
    else
    theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness"))))
    

    【讨论】:

    • 好吧,如果你处理的是 int32 属性,string.Empty 不会有什么好处......
    【解决方案3】:

    您知道,我一直在 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 扩展类非常容易编写,并且会极大地简化您的工作。 相信我。我会骗你吗? :)

    【讨论】:

    • 我认为这行不通,因为如果数据库中的列是 NULL,那么调用 GetInt32() 将导致异常。您无法将 GetInt32() 调用与 DBNull.Value 进行比较并对此做出反应....
    • 它起作用的原因是因为我们使用了短路 ?: 运算符。如果它不为空,我们将其返回。否则,我们返回默认值。
    • 实际上,至少对于 SQL Server,它确实 NOT 工作 - 如果基础 db 列包含 NULL,GetInt32() 将抛出异常,并且您无法比较 GetInt32( ) 调用 DBNull.Value - VS2008 会抱怨甚至不编译它。
    • 但我绝对喜欢将其包装在扩展方法中的想法 - 让生活更轻松!
    • 我尝试使用您的方法并将 reader.GetInt32() 的输出与 SQL Server 中的 DBNull.Value 进行比较:我得到的只是此错误消息:运算符 '!=' 不能应用于操作数'int' 和 'System.DBNull' 类型的
    【解决方案4】:

    这个通用版本可能有用:

        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 语句。我认为您希望尽快知道架构结构是否已更改,而不是将默认值推送到系统的其他部分。
    【解决方案5】:

    IsDbNull(int) 通常比使用 GetSqlInt32 之类的方法然后与 DBNull.Value 比较或使用它自己的 .IsNull Like 慢得多:

        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 以便能够只有一两个模板化方法。

    如果有人有一个或两个功能技巧的想法,请说出来:-)

    【讨论】:

      【解决方案6】:

      这是我们在 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
      

      【讨论】:

        【解决方案7】:

        现在,如果数据库返回 null,您可能想要 null,因此您可以使用 Nullable&lt;int&gt;

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

        【讨论】:

          猜你喜欢
          • 2020-08-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-07
          • 2021-04-29
          • 1970-01-01
          相关资源
          最近更新 更多