【问题标题】:Handle DBNull in C#在 C# 中处理 DBNull
【发布时间】:2011-01-26 20:24:58
【问题描述】:

有没有更好/更清洁的方法来做到这一点?

int stockvalue = 0;
if (!Convert.IsDBNull(reader["StockValue"]))
    stockvalue = (int)reader["StockValue"];

【问题讨论】:

标签: c# datareader dbnull


【解决方案1】:

最短的(恕我直言)是:

int stockvalue = (reader["StockValue"] as int?) ?? 0;

解释:

  • 如果 reader["StockValue"]int 类型,则返回值,并且 "??"运算符将返回结果
  • 如果 reader["StockValue"] 不是 int 类型(例如 DBNull),将返回 null,并且 "??"运算符将返回值 0(零)。

【讨论】:

  • @Philippe:如果列的类型更改为 Double 之类的会怎样?
  • +1 - 这是我编写代码的方式,尽管如果空值有效,则不需要默认值; int? stockvalue = reader["StockValue"] as int?
  • @John:它会返回 0。但无论您选择哪种解决方案,如果列更改数据类型,您就完蛋了,除非您调用 Convert.ToInt32() 或其他方法.
  • 但是通过显式拆箱强制转换int x = (int)obj,当列的类型更改时,您的代码将中断,允许您将其更正为double x = (double)obj; @JohnSaunders 提出红旗是正确的,因为 as int? 方法会默默地接受这种变化;只有当有人注意到不应该为零的零值时,您才能捕捉到它。
  • phoog 完全正确。如果列不是您期望的 int,则结果始终为 0。想象一下其他人试图阅读此代码以找出为什么结果始终为 0?下面Digicoder的解决方案更容易阅读,也更灵活。只需使用基本的 if 速记。
【解决方案2】:

我的处理方式是

int? stockvalue = reader["StockValue"] as int?;

非常简单,干净,一行。如果由于某种原因我绝对不能有一个空值(我发现这通常是不好的推理,因为我宁愿知道一个值是否有意义,或者它是否被单元化为原始类型)我会这样做:

int stockvalue = (reader["StockValue"] as int?).GetValueOrDefault(-1);

【讨论】:

  • 啊,我在使用(int?) reader["ColumnName"] 时遇到了异常,并开始寻找正确的方法。这应该被接受为答案。
  • 优秀。这也保留了 DBNull 状态,以防有人有兴趣知道这一点。这里的一些其他解决方案放弃了这一点。
【解决方案3】:

我几天前写了一个扩展方法。通过使用它,您可以这样做:

int? stockvalue = reader.GetValue<int?>("StockValue");

这是扩展方法(根据您的需要进行修改):

public static class ReaderHelper
{
    public static bool IsNullableType(Type valueType)
    {
        return (valueType.IsGenericType &&
            valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
    }

    public static T GetValue<T>(this IDataReader reader, string columnName)
    {
        object value = reader[columnName];
        Type valueType = typeof(T);
        if (value != DBNull.Value)
        {
            if (!IsNullableType(valueType))
            {
                return (T)Convert.ChangeType(value, valueType);
            }
            else
            {
                NullableConverter nc = new NullableConverter(valueType);
                return (T)Convert.ChangeType(value, nc.UnderlyingType);
            }
        }
        return default(T);
    }
}

【讨论】:

  • 我非常喜欢 C#,并且有一两个地方有类似的代码,但是我们需要编写这样的代码绝对是迟钝的。我希望随着 DLR 的不断进步,有一天我们不再需要编写这样的代码来进行类型争论。
【解决方案4】:
int? stockvalue = (int?)(!Convert.IsDBNull(result) ? result : null);

一种可能的解决方案,以便您确保将 DBNull 传递到您的代码中。对于我们的小组,作为最佳实践,我们尝试在数据库中不允许 NULL 列,除非它确实需要。处理它的编码开销更大,有时只是重新考虑问题就不需要了。

【讨论】:

  • 我认为这个解决方案是最清晰和灵活的。我选择了几乎相同但不允许空值的东西: Int32 aNum = Convert.IsDBNull(row["stockvalue"]) ? 0 : Convert.ToInt32(row["stockvalue"])
【解决方案5】:

是的,您可以使用int? 这样您就可以使用 null 而不是 0 的默认值。由于 stockvalue 的结果可能为 0,因此不会混淆数据库是 0 还是 null。例如像这样(可以为空)我们有一个默认的初始化 -1 来表示没有赋值。就个人而言,我认为这有点危险,因为如果您忘记将其设置为 -1,则会出现数据损坏问题,并且很难追踪。

http://msdn.microsoft.com/en-us/library/2cf62fcy(VS.80).aspx

int? stockvalue = null;

if (!Convert.IsDBNull(reader["StockValue"]))
    stockvalue = (int)reader["StockValue"];

//Then you can check 

if(stockValue.HasValue)
{
  // do something here.
}

【讨论】:

  • +1 表示要使用的正确数据类型。然而,IIRC,DBNull 仍然需要通过一些检查转换为null
  • 使用整数?如何?当 reader["StockValue"] 为 DBNull 时,简单地将 reader["StockValue"] 转换为 (int?) 将引发异常
  • 你可以使用int吗?这样您就不必使用默认初始化为 0。
【解决方案6】:

虽然引用reader["StockValue"] 很方便,但效率不高。它也不是强类型,因为它返回类型 object

相反,在您的代码中,执行以下操作:

int stockValueOrdinal = reader.GetOrdinal("StockValue");
int? stockValue = reader.IsDbNull(stockValueOrdinal) ?
    null : 
    reader.GetInt32(stockValueOrdinal);

当然,最好一次获取所有序数,然后在整个代码中使用它们。

【讨论】:

  • @John:在您对我的回答的评论中,您问我“如果列变为双倍怎么办?”。如果列包含非 int 类型的值,您在此处提供的代码将引发异常。 (另外,小细节:数据读取器上没有 .GetInt() 方法。它应该是 .GetInt32())
  • @Philippe:抛出异常是准确地如果列类型发生变化而代码没有发生变化时代码应该做什么。当出现严重错误时返回零是一个非常糟糕的主意。
【解决方案7】:
int stockvalue = reader["StockValue"] != DbNull.Value ? Convert.ToInt32(reader["StockValue"]) : 0;

【讨论】:

    【解决方案8】:

    这是一种方法。

    int stockvalue = Convert.IsDbNull(reader["StockValue"]) ? 0 : (int)reader["StockValue"];
    

    你也可以使用 TryParse

    int stockvalue = 0
    Int32.TryParse(reader["StockValue"].ToString(), out stockvalue);
    

    让我们知道哪种方式适合您

    【讨论】:

    • 我不介意投反对票,请发表评论说我如何改进这个答案。
    • 这里绝对没有理由使用 DV,因为您的答案是正确的解决方案。
    • 是的,没有错。我投票给你以平衡它。 :-)
    • 我没有投反对票,但这是一段非常低效的代码。您的第一行调用 reader[] 索引器两次。第二个使用非常低效的字符串转换,这是完全没有必要的。
    • 好点,我通常不会在我的应用程序中使用任何类型的数据读取器。而是选择基于对象的后端数据表示。
    【解决方案9】:

    您可以直接在数据库查询中进行此转换,从而完全避免这种特殊情况。

    但我不会称其为“更清洁”,除非您可以在代码中始终如一地使用该表单,因为从数据库返回“0”而不是 NULL 会丢失信息。

    【讨论】:

    • 关于丢失信息的论点确实有道理。在数据库应用程序中,“0”的值不同于 NULL。 1 表示根本没有值,而 1 是恰好为零的值。
    【解决方案10】:

    使用Nullable&lt;int&gt; 类型...int? 简称

    【讨论】:

      【解决方案11】:

      不是真的。您可以将其封装在一个方法中:

      public int getDBIntValue(object value, int defaultValue) {
        if (!Convert.IsDBNull(value)) {
          return (int)value;
        }
        else {
          return defaultValue;
        }
      

      然后这样称呼它:

      stockVaue = getDBIntVaue(reader["StockValue"], 0);
      

      或者您可以在查询中使用coalesce 来强制返回值非空。

      编辑 - 根据收到的 cmets 纠正哑代码错误。

      【讨论】:

      • (我没有对你投反对票,但是)一方面,你在“value”参数中传入了 reader[“StockValue”],但随后忽略了函数内部的“value”参数并使用"reader["StockValue"]" 代替,所以它不会编译。
      • @Ray:您的getDBIntValue 将始终尝试从reader["StockValue"] 中提取int,无论传入什么。您还拥有关键字default 作为您的参数之一,它不会编译(你必须把它改成@default,或者——更好——只是改变名字)。
      • @John - 我更正了我犯的复制粘贴错误。然而,“如果你不知道我不会告诉你”的事情并不能帮助我(或其他阅读这些 cmets 的人)学习。
      • @Dan:大概他意识到它总是会得到一个 int,因此名称 getDBIntVaue(顺便说一句,Ray,请添加缺少的 'l'。拼写错误很麻烦我)。
      • 这将教会我在不检查我的代码的情况下只输入和发布 - 对不起愚蠢的错误 - 我认为它现在已经修复了。
      【解决方案12】:

      我的项目中有以下两种扩展方法:

          public static T GetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor)
              where T : class 
          {
              T value;
              if (dataReader.TryGetValueSafe(columnName, valueExtractor, out value))
              {
                  return value;
              }
      
              return null;
          }
      
          public static bool TryGetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor, out T value)
          {
              int ordinal = dataReader.GetOrdinal(columnName);
      
              if (!dataReader.IsDBNull(ordinal))
              {
                  // Get value.
                  value = valueExtractor.Invoke(dataReader, ordinal);
      
                  return true;
              }
      
              value = default(T);
              return false;
          }
      

      用法可以是这样的:

      string companyName = dataReader.GetValueSafe("CompanyName", (reader, ordinal) => reader.GetString(ordinal));
      

      【讨论】:

      • 有趣但非常冗长,我在广泛使用 DataReaders 时创建了一个与此类似的类,但更简洁的是它以 reader.Get&lt;int&gt;("ReaderField") 运行并使用 ChangeType 方法工作
      • 好主意 +1,但我认为我的实现会快一点。
      【解决方案13】:
      int? stockValue = reader["StockValue"] == null || reader["StockValue"] == DBNull.Value ? null : (int?)reader["StockValue"];
      

      【讨论】:

        猜你喜欢
        • 2012-04-06
        • 1970-01-01
        • 2023-03-09
        • 2017-11-01
        • 2023-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多