如何防范"Object cannot be cast from DBNull to other types"?
How can I guard against "Object cannot be cast from DBNull to other types"?
我有这段代码,我在 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"]);
}
. . .
...但是我得到,“对象无法从 DBNull 转换为其他类型”在最后一次尝试分配(到 pauv.Week2Usage)的特定迭代中。
该字段确实出现在 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; }
}
...代码对大多数记录运行良好。
什么会导致这个异常,我该如何防范?
原因是数据库 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 class 进一步简化事情:
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");
将最后一个if
条件更改为
if (!(priceAndUsageVarianceRow["Week2Usage"] is DBNull) && null != priceAndUsageVarianceRow["Week2Usage"])
{
pauv.Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]);
}
只是将泛型添加到@Übercoder 的回答中。
您的扩展方法;
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)));
}
调用示例;
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);
}
我有这段代码,我在 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"]);
}
. . .
...但是我得到,“对象无法从 DBNull 转换为其他类型”在最后一次尝试分配(到 pauv.Week2Usage)的特定迭代中。
该字段确实出现在 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; }
}
...代码对大多数记录运行良好。
什么会导致这个异常,我该如何防范?
原因是数据库 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 class 进一步简化事情:
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");
将最后一个if
条件更改为
if (!(priceAndUsageVarianceRow["Week2Usage"] is DBNull) && null != priceAndUsageVarianceRow["Week2Usage"])
{
pauv.Week2Usage = Convert.ToDouble(priceAndUsageVarianceRow["Week2Usage"]);
}
只是将泛型添加到@Übercoder 的回答中。
您的扩展方法;
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)));
}
调用示例;
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);
}