简体   繁体   English

如何防止“无法将对象从DBNull强制转换为其他类型”?

[英]How can I guard against “Object cannot be cast from DBNull to other types”?

I've got this code, where ISTM I'm defensively coding against assigning nulls: 我有以下代码,在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"]);
    }
    . . .

...yet I get, " Object cannot be cast from DBNull to other types " on a particular iteration on the last attempted assignment (to pauv.Week2Usage). ...但我得到的是,在最后一次尝试的分配(到pauv.Week2Usage)的特定迭代中,“ 无法将对象从DBNull强制转换为其他类型 ”。

That field does appear in the class: 该字段确实出现在班级中:

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

...and the code runs fine for most of the records. ...并且大多数记录的代码运行良好。

What would cause this exception, and how can I guard against it? 是什么导致此异常,我该如何防范?

The cause is that Data base NULL values are returned not as CLR null but as an object of type DBNull so you really want to test against that. 原因是数据库NULL值不是作为CLR null值返回,而是作为DBNull类型的对象返回,因此您真的想对此进行测试。 The safe thing, depending on circumstances would be to test against both null and whether the object is of type DBNull 根据情况,安全的做法是同时测试null和对象是否为DBNull类型

Write yourself a little helper Function: 给自己写一个小助手函数:

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

and modify your code thus: 然后修改您的代码:

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

As you need to do this sort of testing all the time you might want to borrow my DataRowEx class that simplfies things further: 当您需要一直进行这种测试时,您可能想借用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);
    }
}

Then your code becomes: 然后您的代码变为:

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

Just adding Generics to @Übercoder's answer. 只需在@Übercoder的答案中添加泛型即可。

Your extension method; 您的扩展方式;

public static T GetValueOrDefault<T>(this IDataRecord row, string fieldName)
{
    int ordinal = row.GetOrdinal(fieldName);
    return row.GetValueOrDefault<T>(ordinal);
}

public static T GetValueOrDefault<T>(this IDataRecord row, int ordinal)
{
    return (T)((row.IsDBNull(ordinal) ? default(T) : row.GetValue(ordinal)));
}

Sample call; 样品通话;

DataReader reader = //your database call
var employees =  new List<Employee>();
while (reader.Read())
{
    var employee = new Employee
    {
        Id = reader.GetValueOrDefault<int>("EmpId"),
        Name = reader.GetValueOrDefault<string>("Name")
    };
    employees.Add(employee);
}

Change the last if condition to 将最后一个if条件更改为

if (!(priceAndUsageVarianceRow["Week2Usage"] is DBNull) && null != priceAndUsageVarianceRow["Week2Usage"])
{
    pauv.Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM