DBNull有什么意义?

时间:2010-12-20 10:25:26

标签: .net history dbnull rationale

在.NET中有null引用,它在任何地方用于表示对象引用为空,然后有DBNull,数据库驱动程序使用它(和其他几个)表示......几乎是一样的东西。当然,这会产生很多混乱,转换程序也必须被淘汰等等。

那么为什么最初的.NET作者决定这样做呢?对我来说没有任何意义。他们的文档也没有意义:

  

DBNull类表示不存在的值。例如,在数据库中,表行中的列可能不包含任何数据。也就是说,该列被认为根本不存在而不仅仅是没有值。 DBNull对象表示不存在的列。此外,COM interop使用DBNull类来区分VT_NULL变量(表示不存在的值)和VT_EMPTY变量(表示未指定的值)。

关于“不存在的列”的废话是什么?存在一列,它只是没有特定行的值。如果它不存在,我会尝试访问特定的单元格,而不是DBNull!我可以理解需要区分VT_NULLVT_EMPTY,但为什么不改为成为COMEmpty类呢?在整个.NET框架中,这将更加适合。

我错过了什么吗?任何人都可以解释为什么发明DBNull以及它有助于解决哪些问题?

6 个答案:

答案 0 :(得分:164)

我不同意这里的趋势。我会记录在案:

  

我不同意DBNull有任何有用的目的;它增加了不必要的混淆,同时几乎没有任何价值。

经常提出这样的论点:null是一个无效的引用,而DBNull是一个空的对象模式; 既不是真的。例如:

int? x = null;

这不是“无效的参考”;它是null值。确实null意味着你想要它意味着什么,坦率地说,使用可能null的值完全没有问题(事实上,即使在SQL中我们需要正确使用null - 这里什么都没改变。同样,“空对象模式”只有在OOP术语中实际将其视为对象时才有意义,但如果我们的值可以是“我们的值,或DBNull”那么它必须是{ {1}},所以我们不能对它做任何有用的事情。

object有很多不好的事情:

  • 它强制您使用DBNull,因为只有object可以保留object 其他值
  • “可能是值或DBNull”与“可能是值或DBNull
  • 之间没有真正的区别
  • 它源于1.1(可以为空的类型)的论点毫无意义;我们可以在1.1
  • 中完美地使用null
  • 大多数API都有“它是否为空?”方法,例如nullDBDataReader.IsDBNull - 这些方法实际上都不需要DataRow.IsNull存在于API
  • DBNull在空合并方面失败;如果值为DBNull
  • ,则value ?? defaultValue无效
  • DBNull不能在可选参数中使用,因为它不是常量
  • DBNull.Value的运行时语义与DBNull的语义相同;特别是,null实际上等于DBNull - 所以它完成表示SQL语义的工作
  • 它经常强制将值类型值装箱,因为它过度使用了DBNull
  • 如果您需要测试object,您可能只测试了DBNull
  • 它会导致命令参数等问题出现大问题,如果参数的值为null则不会被发送......好吧,这里有一个想法:如果你不这样做想要发送参数 - 不要将它添加到参数集合
  • 我能想到的每个ORM在没有任何需要或使用null的情况下都能很好地工作,除非在与ADO.NET代码交谈时造成额外的麻烦

唯一甚至远程引人注目的论据,我曾经认为可以证明这种价值的存在DBNull ,传递值时创建一个新行; DataTable表示“使用默认值”,null明确为空 - 坦率地说,此API可能已针对此案例进行了特定处理 - 例如假想DBNull会更好而不是引入一个无条件感染大量代码的DataRow.DefaultValue

同样,DBNull.Value场景充其量也是微不足道的;如果您正在执行标量方法,则期望结果。在没有行的情况下,返回ExecuteScalar似乎并不太可怕。如果你绝对需要消除“无行”和“返回一个单独”之间的歧义,那就是阅读器API。

这艘船很久以前就已经航行了,修理它已经太晚了。但!请认为每个人都同意这是一个“显而易见”的事情。很多开发人员看到BCL这个奇怪的皱纹中的价值。

我真的想知道所有这些都源于两件事:

  • 必须使用单词null而不是VB中涉及“null”的内容
  • 能够使用“看起来像SQL”的Nothing语法,而不是非常棘手的if(value is DBNull)

要点:

有3个选项(if(value==null)null或实际值)仅在有真实示例需要消除3种不同情况之间的歧义时才有用。我还没有看到我需要表示两个不同的“空”状态的情况,因此DBNull完全是冗余的,因为DBNull已经存在并且具有更好的语言和运行时支持。

答案 1 :(得分:45)

关键是在某些情况下,数据库值为null和.NET Null之间存在差异。

例如。如果您使用ExecuteScalar(返回结果集中第一行的第一列)并且您返回null,则表示执行的SQL未返回任何值。如果你得到DBNull,它意味着SQL返回一个值,它是NULL。你需要能够分辨出来。

答案 2 :(得分:13)

DbNull表示没有内容的框; null表示该盒子不存在。

答案 3 :(得分:1)

您使用DBNull来丢失数据。 .NET语言中的Null意味着没有对象/变量的指针。

DBNull缺少数据:http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx

缺少数据对统计数据的影响:

http://en.wikipedia.org/wiki/Missing_values

答案 4 :(得分:0)

CLR null和DBNull之间存在一些差异。首先,关系数据库中的null具有不同的“等于”语义:null不等于null。 CLR null IS等于null。

但我怀疑主要原因是参数默认值在SQL服务器中的工作方式以及提供程序的实现方式。

要查看差异,请使用具有默认值的参数创建过程:

CREATE PROC [Echo] @s varchar(MAX) = 'hello'
AS
    SELECT @s [Echo]

结构良好的DAL代码应该将命令创建与使用分开(以允许多次使用相同的命令,例如,有效地多次调用存储过程)。编写一个返回表示上述过程的SqlCommand的方法:

SqlCommand GetEchoProc()
{
    var cmd = new SqlCommand("Echo");
    cmd.Parameters.Add("@s", SqlDbType.VarChar);
    return cmd;
}

如果现在在不设置@s参数的情况下调用该命令,或者将其值设置为(CLR)null,则它将使用默认值“hello”。另一方面,如果将参数值设置为DBNull.Value,它将使用它并回显DbNull.Value。

由于使用CLR null或数据库null作为参数值有两个不同的结果,因此不能仅使用其中一个表示两种情况。如果CLR null是唯一的那个,它必须以DBNull.Value今天的方式工作。向提供者指示“我想使用默认值”的一种方法可能是根本不声明参数(具有默认值的参数当然有意义地描述为“可选参数”),但是在在缓存和重用命令对象的情况下,这会导致删除并重新添加参数。

我不确定我是否认为DBNull是个好主意,但很多人都不知道我在这里提到的事情,所以我觉得值得一提。

答案 5 :(得分:0)

要回答您的问题,您必须考虑为什么DBNull甚至存在?

在狭窄的用例中,DBNull是必需的。否则,事实并非如此。大多数人从不需要DBNull。我绝对不允许将它们输入我设计的数据存储中。我总是有一个值,因此我的数据永远不会是“ ”,我总是选择一个有意义的类型“默认值”,而且我也不必这样做,这荒唐的重复检查代码中的所有内容两次,一次是对象null,再次是DBNull,然后再将对象转换为实际的数据类型(例如Int等)。

在一种情况下,您可能需要DBNull ...如果您使用某些SQL统计功能(例如:中位数,平均值)..他们特别对待DBNull。.请看一下这些文档..某些功能不会在统计信息的总计数中包括一个DBNull ...例如:(87总和/ 127总和)(87总和/ 117总和)..区别在于这些列值中有10个是DBNull ...您可以看到这会改变统计结果。

我不需要使用DBNull设计数据库。如果我需要统计结果,我会为该项目明确发明或添加一列,例如“ UserDidProvideValue”,因为该项目不存在,因此需要某种特殊处理(例如,我的总数为117,即为标记字段的总和) UserDidProvideValue = true)...哈哈哈哈-在我下一世作为宇宙的统治者时-决不会允许DBNull逃离SQL领域...整个编程世界现在担负着检查所有事情的重担...何时您是否曾经有一个移动应用程序或台式机应用程序或网站需要一个“空”整数? -从来没有...

相关问题