【问题标题】: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 的最佳方法是什么。

【问题讨论】:

标签: c# .net sql-server


【解决方案1】:

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

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

int? value = 5;

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

  1. 如果一个类型可以为空,如果一个值为DBNull,“as”关键字返回null
  2. 虽然是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 和空字符串之间的区别很重要。例如,将一个值设置为一个空字符串,而不是一个从未被设置的值。
                  猜你喜欢
                  • 1970-01-01
                  • 2012-09-12
                  • 2012-09-18
                  • 2011-05-20
                  • 2016-05-26
                  • 2018-12-03
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-03-01
                  相关资源
                  最近更新 更多