如果值为null,如何将值返回给sqldatareader?

如果值为null,如何将值返回给sqldatareader?,sql,sql-server,vb.net,sql-server-2008,Sql,Sql Server,Vb.net,Sql Server 2008,我目前正在使用sql数据读取器(在vb.net中)通过存储过程从sql Server 2008数据库中提取文章对象。此对象的一部分包括以下两个属性: theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness")))) theArticle.Relevance = ((myReader.GetInt32(myReader.GetOrdinal("Relevance")))) 我的问题是,真实性和

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

theArticle.Truthfulness = ((myReader.GetInt32(myReader.GetOrdinal("Truthfulness"))))
theArticle.Relevance = ((myReader.GetInt32(myReader.GetOrdinal("Relevance"))))
我的问题是,真实性和相关性可能会返回空值,这会导致函数失效

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


如何适应数据库中的空值,使其不会溢出?

您可以使用
.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

您是否检查了SqlDataReader.IsDBNull方法?可能是这样的:

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

你知道,我在甲骨文公司一直在处理这个问题。为了清理代码,我编写了一组扩展方法来简化操作:

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;
    }
}
为要返回的每个类型创建单独的重载。我主要处理字符串、int、date和decimal。记住YAGNI(您不需要处理读取器支持的所有类型,只需要处理您实际使用的类型。)


对于SQL Server,这样的扩展类非常容易编写,并且将大大简化您的工作。相信我。我会对你撒谎吗?:)

以下是我们在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
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,以便只需要一个或两个模板化方法


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

此通用版本可能有用:

    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;
    }
private T valuerDefault(System.Data.IDataReader rdr,string columnName)
{
T vod=默认值(T);
尝试
{
int idx=rdr.GetOrdinal(columnName);
如果(!rdr.IsDBNull(idx))
返回(T)rdr[idx];
}
catch(IndexOutOfRangeException){}
返回视频点播;
}

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

现在,如果数据库返回空值,您可能需要空值,因此您可以使用可为空的


嗯,string.Empty如果处理int32属性……或者捕获异常并处理它,那么它没有多大用处。是的,你也可以这样做——但避免异常总比捕获并处理异常好(一般来说)@marc_s:同意你对我答案的评论。删除它。谢谢你讲清楚了。您的评论是“我不认为这会起作用,因为如果db列为NULL,.GetInt32()调用将失败,出现异常-您将无法返回NULL值,然后可以将其输入到”?“运算符…”现在您可能会返回一个可为NULL的int:我认为这不会起作用,因为如果数据库中的列为NULL,然后,对GetInt32()的调用将导致异常。您无法将GetInt32()调用与DBNull.Value进行比较并对其作出反应……它之所以有效,是因为我们使用了短路?:运算符。如果它不是空的,我们就返回它。否则,我们将返回默认值。实际上,至少对于SQL Server,它不起作用-如果基础db列包含NULL,则GetInt32()将引发异常,并且您无法比较GetInt32()调用DBNull.Value-VS2008会抱怨,甚至不会编译它。但我绝对喜欢用扩展方法来包装它,这让生活变得更简单!我尝试使用您的方法将reader.GetInt32()的输出与SQL Server中的DBNull.Value进行比较:我得到的只是以下错误消息:运算符“!=”无法应用于“int”和“System.DBNull”类型的操作数。除IndexOutOfRangeException catch语句外,我喜欢此选项。我认为您应该尽快知道模式结构是否已更改,而不是将默认值推送到系统的其他部分。
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);
    }
}