处理DBNull的最佳方法是什么

时间:2020-03-05 18:43:00  来源:igfitidea点击:

我经常遇到处理从SqlDataAdapters返回的DataRows的问题。当我尝试使用如下代码填充对象时:

DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;

在这种情况下,处理" DBNull"的最佳方法是什么。

解决方案

回答

可空类型是好的,但仅适用于不可为空的类型。

要使类型"可为空",请在该类型后添加一个问号,例如:

int? value = 5;

我也建议使用关键字"as"而不是强制类型转换。我们只能在可为空的类型上使用" as"关键字,因此请确保我们正在投射已经为可为空的内容(例如字符串),或者如上所述使用可为空的类型。原因是

  • 如果类型为可为空,则如果值为DBNull,则关键字" as"将返回null。
  • 尽管仅在某些情况下,它比强制转换要快得多。单独使用这绝不是使用as的充分理由,但再加上上面的原因,它很有用。

我建议做这样的事情

DataRow row = ds.Tables[0].Rows[0];
string value = row as string;

在上述情况下,如果" row"以" DBNull"的形式返回,则" value"将变为" null",而不是引发异常。请注意,如果数据库查询更改了要返回的列/类型,则使用as将导致代码默默地失败,并使值简单为'null',而不是在返回错误数据时抛出适当的异常,因此建议我们有测试以其他方式验证查询,以确保随着代码库的发展数据的完整性。

回答

我们也可以使用Convert.IsDBNull(MSDN)进行测试。

回答

添加对" System.Data.DataSetExtensions"的引用,该引用增加了Linq对查询数据表的支持。

就像这样:

string value = (
    from row in ds.Tables[0].Rows
    select row.Field<string>(0) ).FirstOrDefault();

回答

我通常会编写包装内置的Convert类的自己的ConvertDBNull类。如果值为DBNull,则如果引用类型为DBNull,则返回null;如果值为数值类型,则返回默认值。
例子:
ConvertDBNull.ToInt64(object obj)返回Convert.ToInt64(obj),除非obj是DBNull,在这种情况下它将返回0。

回答

如果我们不使用可为空的类型,那么最好的办法是检查该列的值是否为DBNull。如果它是DBNull,则将引用设置为对相应数据类型使用null / empty的引用。

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

就像Manu所说的那样,我们可以使用每种类型的重载convert方法创建一个convert类,这样就不必在if / else块中添加代码了。

但是,我要强调的是,如果可以使用可空类型,那是更好的选择。原因是对于非空类型,我们将不得不求助于"魔术数字"来表示空。例如,如果要将列映射到int变量,则如何表示DBNull?通常,我们不能使用0,因为0在大多数程序中具有有效的含义。我经常看到人们将DBNull映射到int.MinValue,但这也可能有问题。我最好的建议是这样的:

  • 对于数据库中可以为空的列,请使用可为空的类型。
  • 对于数据库中不能为空的列,请使用常规类型。

可空类型用于解决此问题。话虽如此,如果我们使用的是较旧版本的框架,或者是为不使用可空类型的人工作的,那么代码示例将助我们一臂之力。

回答

由于某些原因,我在对DBNull.Value进行检查时遇到了问题,因此我做了一些稍有不同的事情,并利用了DataRow对象中的一个属性:

if (row.IsNull["fooColumn"])
{
   value = string.Empty();
}
{
else
{
   value = row["fooColumn"].ToString;
}

回答

如果我们控制着返回结果的查询,则可以使用ISNULL()返回非空值,如下所示:

SELECT 
  ISNULL(name,'') AS name
  ,ISNULL(age, 0) AS age
FROM 
  names

如果情况可以容忍这些不可思议的值来代替NULL,则采用这种方法可以解决整个应用程序中的问题,而不会使代码混乱。

回答

我总是发现,仅使用三元运算符,使用版本的If / Else检查就可以清楚,简洁且没有问题。将所有内容都保留在一行上,包括在该列为null时分配默认值。

因此,假设一个名为" MyCol"的可为空的Int32列,如果该列为null,我们想在其中返回-99,但是如果该列不为null,则返回整数值:

return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);

这与上面的If / Else赢家使用的方法相同,但是我发现如果我们正在从数据读取器中读取多个列,那么将所有列读取行依次排列并排成一行是一个真正的好处,因为它更容易发现错误:

Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]);
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]);
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]);

回答

如果我们担心在期望字符串时获取DBNull,则一种选择是将DataTable中的所有DBNull值转换为空字符串。

这样做很简单,但是会增加一些开销,尤其是在处理大型数据表时。如果我们感兴趣,请查看此链接,该链接显示了如何执行此操作

回答

布拉德·艾布拉姆斯(Brad Abrams)在几天前发布了一些相关的内容
http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

在摘要中"使用System.DBNull避免。请改用Nullable。"

这是我的两分钱(未经测试的代码:))

// Or if (row["fooColumn"] == DBNull.Value)
if (row.IsNull["fooColumn"])
{
   // use a null for strings and a Nullable for value types 
   // if it is a value type and null is invalid throw a 
   // InvalidOperationException here with some descriptive text. 
   // or dont check for null at all and let the cast exception below bubble  
   value = null;
}
else
{
   // do a direct cast here. dont use "as", "convert", "parse" or "tostring"
   // as all of these will swallow the case where is the incorect type.
   // (Unless it is a string in the DB and really do want to convert it)
   value = (string)row["fooColumn"];
}

还有一个问题...我们不使用ORM的任何原因是什么?

回答

DBNull像其他所有东西一样实现.ToString()。没必要做任何事。而不是强制转换,而是调用对象的.ToString()方法。

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

变成:

DataRow row = ds.Tables[0].Rows[0];
string value = row.ToString()

DBNull.ToString()返回string.Empty

我想这是我们要寻找的最佳实践