c# - 如何防范 "Object cannot be cast from DBNull to other types"?

标签 c# double dbnull defensive-programming null-check

我有这段代码,我在 ISTM 中防御性编码以防止分配空值:

foreach (DataRow priceAndUsageVarianceRow in _dtUsage.Rows)
{
    //var pauv = new PriceAndUsageVariance
    //{
    //    Description = priceAndUsageVarianceRow["Description"].ToString(),
    //    Week1Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week1Usage"]),
    //    Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]),
    //    UsageVariance = Convert.ToDouble(priceAndUsageVarianceRow["UsageVariance"]),
    //    Week1Price = Convert.ToDecimal(priceAndUsageVarianceRow["Week1Price"]),
    //    Week2Price = Convert.ToDecimal(priceAndUsageVarianceRow["Week2Price"]),
    //    PriceVariance = Convert.ToDecimal(priceAndUsageVarianceRow["PriceVariance"]),
    //    PriceVariancePercentage = Convert.ToDouble(priceAndUsageVarianceRow["PriceVariancePercentage"])
    //};
    // Got exception with the code above; trying to prevent it with this:
    var pauv = new PriceAndUsageVariance();
    pauv.Description = String.Empty;
    pauv.Week1Usage = 0.0;
    pauv.Week2Usage = 0.0;
    pauv.UsageVariance = 0.0;
    pauv.Week1Price = 0.00M;
    pauv.Week2Price = 0.00M;
    pauv.PriceVariance = 0.00M;
    pauv.PriceVariancePercentage = 0.0;
    if (null != priceAndUsageVarianceRow["Description"])
    {
        pauv.Description = priceAndUsageVarianceRow["Description"].ToString();
    }
    if (null != priceAndUsageVarianceRow["Week1Usage"])
    {
        pauv.Week1Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week1Usage"]);
    }
    if (null != priceAndUsageVarianceRow["Week2Usage"])
    {
        pauv.Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]);
    }
    . . .

...但我在上次尝试分配(给 pauv.Week2Usage)的特定迭代中得到“对象无法从 DBNull 转换为其他类型”。

该字段确实出现在类中:

public class PriceAndUsageVariance
{
    public String Description { get; set; }
    public Double Week1Usage { get; set; }
    public Double Week2Usage { get; set; }
    public Double UsageVariance { get; set; }
    public Decimal Week1Price { get; set; }
    public Decimal Week2Price { get; set; }
    public Decimal PriceVariance { get; set; }
    public Double PriceVariancePercentage { get; set; }
}

...代码对大多数记录运行良好。

什么会导致此异常,我该如何防范?

最佳答案

原因是数据库 NULL 值不是作为 CLR null 返回,而是作为 DBNull 类型的对象返回,因此您真的想针对它进行测试。根据具体情况,安全的做法是针对 null 以及对象是否属于 DBNull

类型进行测试

自己写一个小辅助函数:

public static class DbNullExt
{
    public static bool IsNullData(this object obj)
    {
        return obj == null || obj is DBNull;
    }
}

然后修改你的代码:

if (!IsNullData(priceAndUsageVarianceRow["Description"]))
{
    pauv.Description = priceAndUsageVarianceRow["Description"].ToString();
}
if (!IsNullData(priceAndUsageVarianceRow["Week1Usage"]))
{
    pauv.Week1Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week1Usage"]);
}
if (!IsNullData(priceAndUsageVarianceRow["Week2Usage"]))
{
    pauv.Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]);
}

由于您需要一直进行此类测试,因此您可能想借用我的 DataRowEx 类来进一步简化事情:

public static class DataRowEx
{
    public static string String(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return null;

        return obj.ToString();
    }

    public static Int32 Int32(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return (Int32)obj;
    }

    public static Decimal Decimal(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return Convert.ToDecimal(obj);
    }

    public static Double Double(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return Convert.ToDouble(obj);
    }

    public static Single Single(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return Convert.ToSingle(obj);
    }

    public static bool Bool(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return false;

        if (obj is int)
            return (int) obj != 0;

        return (bool)obj;
    }

    public static DateTime DateTime(this DataRow row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return System.DateTime.MinValue;

        return (DateTime)obj;
    }

    public static object ToType(this DataRow row, Type targetType, string columnName)
    {
        if (targetType == typeof(Int32))
            return row.Int32(columnName);

        if (targetType == typeof(bool))
            return row.Bool(columnName);

        if (targetType == typeof(DateTime))
            return row.DateTime(columnName);

        if (targetType == typeof (Decimal))
            return row.Decimal(columnName);

        if (targetType == typeof(Single))
            return row.Double(columnName);

        if (targetType == typeof(Double))
            return row.Double(columnName);

        if (targetType == typeof(string))
            return row.String(columnName);

        return row.String(columnName);
    }

    public static string String(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return null;

        return obj.ToString();
    }

    public static Int32 Int32(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return (Int32)obj;
    }

    public static Decimal Decimal(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return (Decimal)obj;
    }

    public static Double Double(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return (Double)obj;
    }

    public static Single Single(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return 0;

        return (Single)obj;
    }

    public static bool Bool(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return false;

        return (bool)obj;
    }

    public static DateTime DateTime(this DataRowView row, string columnName)
    {
        object obj = row[columnName];

        if (obj is DBNull)
            return System.DateTime.MinValue;

        return (DateTime)obj;
    }

    public static object ToType(this DataRowView row, Type targetType, string columnName)
    {
        if (targetType == typeof(Int32))
            return row.Int32(columnName);

        if (targetType == typeof(bool))
            return row.Bool(columnName);

        if (targetType == typeof(DateTime))
            return row.DateTime(columnName);

        if (targetType == typeof(Decimal))
            return row.Decimal(columnName);

        if (targetType == typeof(Double))
            return row.Double(columnName);

        if (targetType == typeof(Single))
            return row.Single(columnName);

        return row.String(columnName);
    }
}

然后你的代码变成:

pauv.Description = priceAndUsageVarianceRow.String("Description");
pauv.Week1Usage = priceAndUsageVarianceRow.Double("Week1Usage");
pauv.Week2Usage =  priceAndUsageVarianceRow.Double("Week2Usage");

关于c# - 如何防范 "Object cannot be cast from DBNull to other types"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34889433/

相关文章:

c# - 如何知道何时为表单显示消息框?

c# - 定时器比 System.Threading.Timer 更可靠

c# - 将图标添加到 Visual Studio 2017 中的现有菜单命令

c++ - 变量的值为整数和 double

java - 在代码中编写此公式是否有更清晰/更简单的方法?

c# - UWP 模板化控件未获得焦点

c++ - 为什么用 clang 编译时 std::atomic<double> 没有实现?

c# - 在 C# 中处理 DBNull

C#/Oracle10g = null 与 DBNull.Value 与 String.Empty

c# - Null 参数不适用于 Access 和 OLE