处理 DBNull 的最佳方法是啥

Posted

技术标签:

【中文标题】处理 DBNull 的最佳方法是啥【英文标题】:What is the best way to deal with DBNull's处理 DBNull 的最佳方法是什么 【发布时间】:2010-09-06 19:25:34 【问题描述】:

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

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

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

【问题讨论】:

关闭:most-efficient-way-to-check-for-dbnull-and-then-assign-to-a-variable 【参考方案1】:

可以为空的类型很好,但只适用于一开始就不能为空的类型。

要使类型“可为空”,请在该类型上附加一个问号,例如:

int? value = 5;

我还建议使用“as”关键字而不是强制转换。您只能在可空类型上使用“as”关键字,因此请确保您正在转换已经可以为空的内容(如字符串)或使用上述可空类型。原因是

    如果一个类型可以为空,如果一个值为DBNull,“as”关键字返回null。 虽然是only in certain cases,但它是ever-so-slightly faster than casting。这本身并不是使用as 的充分理由,但结合上述原因,它很有用。

我建议做这样的事情

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

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

【讨论】:

这是很老的帖子了,但是值得注意的是,“as”关键字只能用于引用类型和可空类型的转换,不能用于值类型。【参考方案2】:

如果您不使用可空类型,最好检查列的值是否为 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 所说,您可以创建一个转换类,其中每个类型都有一个重载的转换方法,因此您不必在代码中添加 if/else 块。

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

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

可空类型是为了解决这个问题。话虽如此,如果您使用的是旧版本的框架,或者为不了解可空类型的人工作,那么代码示例就可以解决问题。

【讨论】:

if(row["fooColumn"] == DBNull.Value) 行有效,但不正确。它没有被定义 DBNull.Value 应该被实现为单例模式。更好的做法是: if(row["fooColumn"] is DBNull) @doekman - 实际上,DBNull 一个单例类。引用 MSDN:“DBNull 是一个单例类,这意味着只能存在此类的这个 [DBNull.Value] 实例。” msdn.microsoft.com/en-us/library/system.dbnull.value.aspx 如果您想将 dbnull 视为空字符串 row["fooColumn"].ToString() 会这样做。【参考方案3】:

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

这可能是这样的:

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

【讨论】:

+1:这个答案应该得到更多的支持并且可能也被接受 - 绝对 row.Field("column_name") 是处理 null (DBNull.价值)。还有 row.SetField("column_name", value) 用于将值写入行。【参考方案4】:

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

因此,假设一个名为“MyCol”的可空 Int32 列,如果该列为空,我们希望返回 -99,但如果该列不为空,则返回整数值:

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

【讨论】:

【参考方案5】:

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

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

如果您的情况可以容忍这些神奇的值代替 NULL,那么采用这种方法可以解决整个应用程序的问题,而不会弄乱您的代码。

【讨论】:

【参考方案6】:

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

我想这是您正在寻找的最佳实践

【讨论】:

如果值不是字符串,这将不起作用。我正在寻找一般案例的答案。 我同意是的,如果它的字符串保持简单但其他类型不起作用,例如尝试做 bool.Parse(row["fooColumn"].ToString()).【参考方案7】:

值得一提的是,DBNull.Value.ToString() 等于 String.Empty

你可以利用它来发挥你的优势:

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

但是,这仅适用于字符串,对于其他一切我会使用 linq 方式或扩展方法。对于我自己,我编写了一个小扩展方法来检查 DBNull,甚至通过 Convert.ChangeType(...) 进行强制转换

int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);

【讨论】:

【参考方案8】:

通常在使用 DataTables 时,您必须处理这种情况,其中行字段可以为 null 或 DBNull,通常我会这样处理:

string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;

'as' 运算符为无效转换返回 null,例如 DBNull 到字符串,'??'返回 如果第一个为空,则表达式右侧的项。

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

if (row.IsNull["fooColumn"])

   value = string.Empty();


else

   value = row["fooColumn"].ToString;

【讨论】:

【参考方案12】:

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 的任何原因?

【讨论】:

我们现在正在使用 ORM。那时我们还没有 有什么理由不使用 ORM 吗? 我使用原始 ADO .NET 来提高性能。尝试用 EF 或 NH 合并 100 万条记录。【参考方案13】:

您还应该查看扩展方法。 Here 是处理这个场景的一些例子。

推荐read

【讨论】:

【参考方案14】:

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

这很简单,但会增加一些开销,尤其是在处理大型数据表时。如果您有兴趣,请查看此link,它显示了如何操作

【讨论】:

有些时候,了解 Null 和空字符串之间的区别很重要。例如,将一个值设置为一个空字符串,而不是一个从未被设置的值。

以上是关于处理 DBNull 的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

在 Perl 中处理异常的最佳方法是啥?

处理缓存和浏览器后退按钮的最佳方法是啥?

处理 UIActivityIndi​​cator 和多线程的最佳方法是啥?

在graphql中处理文件上传的最佳方法是啥?

在 Java 中处理 GraphQl 响应的最佳方法是啥

编写完成处理程序的最佳方法是啥