将 DBNull 类型转换为其他类型时出现“无法从 DBNull 转换为其他类型的对象”错误。

61

对象不能从DBNull转换为其他类型。

我有一个函数,会抛出上述错误。在存储过程和C#代码中,我处理了所有的null值。

那么它是在哪里得到这个错误的呢?

我可以在catch块中看到错误。但我不明白在以下create()函数的哪一行引发了该错误。

public Boolean Create(DataTO DataTO)
{
    IDbTrans transaction = null;
    IDbCmd IDbCmd;

    string EncryptedPassword = Encrypt(DataTO.txtPwd);
    Base dataAccCom = null;

    try
    {
        dataAccCom = Factory.Create();
        dataAccCom.OpenConnection();
        transaction = dataAccCom.BeginTransaction();
        IDbCmd = dataAccCom.CreateCommand("sp_Register", true);
        dataAccCom.AddParameter(IDbCmd, "op_Id", DbType.Int64, 0, ParameterDirection.Output);
        dataAccCom.AddParameter(IDbCmd, "p_dlstTitle", DbType.String, ReplaceNull(DataTO.dlstTitle));
        dataAccCom.AddParameter(IDbCmd, "p_txtFirstName", DbType.String, ReplaceNull(DataTO.txtFirstName));
        dataAccCom.AddParameter(IDbCmd, "p_txtMiddleName", DbType.String, ReplaceNull(DataTO.txtMiddleName));
        dataAccCom.AddParameter(IDbCmd, "p_txtLastName", DbType.String, ReplaceNull(DataTO.txtLastName));
        dataAccCom.AddParameter(IDbCmd, "p_txtDob", DbType.DateTime, DataTO.txtDob);
        dataAccCom.AddParameter(IDbCmd, "p_txtDesig", DbType.String, ReplaceNull(DataTO.txtDesig));
        dataAccCom.AddParameter(IDbCmd, "p_txtOFlatNo", DbType.String, ReplaceNull(DataTO.txtOFlatNo));
        dataAccCom.AddParameter(IDbCmd, "p_txtOBuild", DbType.String, ReplaceNull(DataTO.txtOBuild));
        dataAccCom.AddParameter(IDbCmd, "p_txtOPost", DbType.String, ReplaceNull(DataTO.txtOPost));
        dataAccCom.AddParameter(IDbCmd, "p_txtOArea", DbType.String, ReplaceNull(DataTO.txtOArea));
        dataAccCom.AddParameter(IDbCmd, "p_txtOCity", DbType.String, ReplaceNull(DataTO.txtOCity));
        dataAccCom.AddParameter(IDbCmd, "p_txtRBuild", DbType.String, ReplaceNull(DataTO.txtRBuild));
        dataAccCom.AddParameter(IDbCmd, "p_txtRPost", DbType.String, ReplaceNull(DataTO.txtRPost));
        dataAccCom.AddParameter(IDbCmd, "p_txtUserID", DbType.String,ReplaceNull(DataTO.txtUserID));
        dataAccCom.AddParameter(IDbCmd, "p_txtPwd", DbType.String, ReplaceNull(EncryptedPassword));
        dataAccCom.ExecuteNonQuery(IDbCmd);
        DataTO.Id = Convert.ToInt64(dataAccCom.GetParameterValue(IDbCmd, "op_Id"));
        transaction.Commit();
        return true;



    }
    catch (System.Exception ex)
    {
        if (transaction != null)
        {
            transaction.Rollback();
        }
        throw ex;
    }
    finally
    {
        transaction = null;
        if (dataAccCom != null)
        {
            dataAccCom.CloseConnection();
        }
        dataAccCom = null;
        IDbCmd = null;
    }
}

public string ReplaceNull(string value)
{
    if (value == null)
    {
        return "";
    }
    else
    {
        return value;
    }
}

public DateTime ReplaceNull(DateTime value)
{
    if (value == null)
    {
        return DateTime.Now;
    }
    else
    {
        return value;
    }
}

public double ReplaceNull(double value)
{
    if (value == null)
    {
        return 0.0;
    }
    else
    {
        return value;
    }
}

请注意,您在 doubleDateTime 上的 ReplaceNull 重载是无用的。这些类型的值永远不可能为 null,因此永远不会通过 if (value == null) 条件。 - Anthony Pegram
除非它们被设置为可空,当然这又是另外一个故事了。 - BoltClock
你是否已经逐步执行该方法?在Visual Studio中,考虑在所有异常上中断(Ctrl+Alt+E),并查看实际导致问题的行。 - Anthony Pegram
好的..这个问题有什么解决方案吗 :-) - Jaison
6
哇哇,不要“扔ex”,只需“扔”即可,否则会丢失一些痕迹。这并不能解决你的问题,但这是一个好习惯需要养成。 - Jeff LaFay
2
我总是在学习新的东西。就在我认为我已经知道了关于异常的所有必要知识时... - eaglei22
8个回答

82

我认为你的输出参数返回了一个空值(DBNull)。像这样添加一个检查

var outputParam = dataAccCom.GetParameterValue(IDbCmd, "op_Id");
if(!(outputParam is DBNull))
     DataTO.Id = Convert.ToInt64(outputParam);

21
也许更好使用Convert.IsDBNull() - abatishchev
我的查询中有多个整数值。我能否以某种方式简化这个过程,还是我必须为每个值都这样做? - Alexander
1
@Alexander,希望你在现在之前已经找到了答案,但是要比这更短可能会变得非常丑陋,但使用三元运算符可以解决问题:DataTO.Id = dataAccCom.GetParameterValue(IDbCmd, "op_Id") == DBNull.Value ? null : Convert.ToInt64(dataAccCom.GetParameterValue(IDbCmd, "op_Id")); - computercarguy

18

我怀疑这一行代码:

DataTO.Id = Convert.ToInt64(dataAccCom.GetParameterValue(IDbCmd, "op_Id"));

这可能是导致问题的原因。存储过程是否将op_Id的值设置为null呢?

为了防止这种情况发生,可以使用Convert.IsDBNull方法。例如:

if (!Convert.IsDBNull(dataAccCom.GetParameterValue(IDbCmd, "op_Id"))
{
 DataTO.Id = Convert.ToInt64(dataAccCom.GetParameterValue(IDbCmd, "op_Id"));
}
else 
{
 DataTO.Id = ...some default value or perform some error case management
}

5

在这里需要检查 DBNull,而不是 null。此外,您的三个 ReplaceNull 方法中有两个没有意义。由于 doubleDateTime 是非空类型,因此检查它们是否为 null 将始终返回 false...


4
错误的原因:在面向对象编程语言中,null 表示没有对对象的引用。DBNull 表示未初始化的变量或不存在的数据库列。来源:MSDN。
我遇到错误的实际代码:
修改前的代码:
    if( ds.Tables[0].Rows[0][0] == null ) //   Which is not working

     {
            seqno  = 1; 
      }
    else
    {
          seqno = Convert.ToInt16(ds.Tables[0].Rows[0][0]) + 1;
     }

改变代码后:

   if( ds.Tables[0].Rows[0][0] == DBNull.Value ) //which is working properly
        {
                    seqno  = 1; 
         }
            else
            {
                  seqno = Convert.ToInt16(ds.Tables[0].Rows[0][0]) + 1;
             }

结论:当数据库返回 null 值时,建议使用 DBNull 类,而不仅仅在 C# 语言中指定为 null。

4

TryParse通常是处理这种类型问题的最优雅的方式:

long temp = 0;
if (Int64.TryParse(dataAccCom.GetParameterValue(IDbCmd, "op_Id").ToString(), out temp))
{
   DataTO.Id = temp;
}     

1

这对我也起作用:

  • reservation.IsFull = rdr["isfull"] is DBNull ? false : (bool)rdr["isfull"];

0

对于从谷歌搜索跳转到此页面的其他用户:

DataRow 还有一个函数 .IsNull("ColumnName")

    public DateTime? TestDt; 
    public Parse(DataRow row)
    {
        if (!row.IsNull("TEST_DT"))
            TestDt = Convert.ToDateTime(row["TEST_DT"]);
    }

0
今天我遇到了这个问题,并通过检查映射类解决了它。我有一个类,其中有5个属性,这些属性被映射到从存储过程返回的5个列。由于这些属性是非空的,所以我一直在收到错误。我将它们改为可为空的,问题得到了解决。
    public int? Pause { get; set; }
    public int? Delay { get; set; }
    public int? Transition { get; set; }
    public int? TransitionTime { get; set; }
    public int? TransitionResolution { get; set; }

将数据类型添加 "?" 以使它们可为空。

其次,我还在存储过程中添加了isNull检查,如下所示:

    isnull(co_pivot.Pause, 0) as Pause,
    isnull(co_pivot.Delay, 0) as Delay,
    isnull(co_pivot.Transition, 0) as Transition,
    isnull(co_pivot.TransitionTime, 0) as TransitionTime,
    isnull(co_pivot.TransitionResolution, 0) as TransitionResolution

希望这能帮到某个人。

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