DBNull 有什么意义?

发布于 2024-10-08 14:41:27 字数 627 浏览 0 评论 0原文

在 .NET 中,有 null 引用,到处都使用它来表示对象引用为空,然后还有 DBNull,它由数据库驱动程序使用(和其他一些)来表示......几乎相同的事情。当然,这会造成很多混乱,并且必须大量制定转换例程等。

为什么原始 .NET 作者决定这样做?对我来说这没有意义。他们的文档也没有意义:

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

“列不存在”是怎么回事?存在一列,只是没有特定行的值。如果它不存在,我会在尝试访问特定单元格时遇到异常,而不是 DBNull!我可以理解需要区分 VT_NULLVT_EMPTY,但是为什么不创建一个 COMEmpty 类呢?这将更适合整个 .NET 框架。

我错过了什么吗?谁能解释一下为什么要发明DBNull以及它有助于解决哪些问题?

In .NET there is the null reference, which is used everywhere to denote that an object reference is empty, and then there is the DBNull, which is used by database drivers (and few others) to denote... pretty much the same thing. Naturally, this creates a lot of confusion and conversion routines have to be churned out, etc.

Why did the original .NET authors decide to make this? To me it makes no sense. Their documentation makes no sense either:

The DBNull class represents a nonexistent value. In a database, for example, a column in a row of a table might not contain any data whatsoever. That is, the column is considered to not exist at all instead of merely not having a value. A DBNull object represents the nonexistent column. Additionally, COM interop uses the DBNull class to distinguish between a VT_NULL variant, which indicates a nonexistent value, and a VT_EMPTY variant, which indicates an unspecified value.

What's this about a "column not existing"? A column exists, it just doesn't have a value for the particular row. If it didn't exist, I'd get an exception trying to access the specific cell, not a DBNull! I can understand the need to differentiate between VT_NULL and VT_EMPTY, but then why not make a COMEmpty class instead? That would be a much neater fit in the whole .NET framework.

Am I missing something? Can anyone shed some light why DBNull was invented and what problems it helps to solve?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

假情假意假温柔 2024-10-15 14:41:27

我不同意这里的趋势。我来记录一下:

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

人们经常提出这样的论点:null 是无效引用,DBNull 是空对象模式; 两者都不是真的。例如:

int? x = null;

这不是“无效引用”;它是一个 null 值。事实上,null 意味着无论你想要它意味着什么,坦率地说,我在处理可能为 null 的值时绝对没有问题(事实上,即使在 SQL 中,我们也需要正确地使用null - 这里没有任何变化)。同样,只有当您实际上将其视为 OOP 术语中的对象时,“空对象模式”才有意义,但如果我们有一个可以是“我们的值,或 DBNull”的值,那么它必须是object,所以我们不能用它做任何有用的事情。

DBNull 有很多不好的地方:

  • 它迫使您使用 object,因为只有 object 可以保存 DBNull > 另一个值
  • 之间没有真正的区别
  • “可能是一个值或DBNull”与“可能是一个值或null”参数 它源于 1.1(可空类型之前)是没有意义的;我们可以在 1.1 中完美地使用 null
  • 大多数 API 都有“它是 null 吗?” 而言存在
  • 方法,例如 DBDataReader.IsDBNullDataRow.IsNull - 这两种方法实际上都不需要 DBNull 就 API DBNull 在空合并方面失败; <代码>值?如果值为 DBNull,defaultValue 不起作用
  • DBNull.Value 不能在可选参数中使用,因为它不是
  • 运行时语义的 常量DBNull 的语义与 null 的语义相同;特别是,DBNull 实际上等于 DBNull - 所以它完成表示 SQL 语义的工作,
  • 它经常强制值类型值装箱,因为它过度使用 object
  • 如果您需要测试 DBNull,您不妨只测试 null
  • 它会导致巨大的问题像命令参数这样的东西,有一个非常愚蠢的行为,如果一个参数有一个 null 值,它就不会被发送......好吧,这里有一个想法:如果你不想发送一个参数 - 不要将其添加到参数集合中
  • 我能想到的每个 ORM 都可以完美地工作,无需任何需要或使用 DBNull,除非作为与 ADO.NET 代码对话时的额外麻烦

这是我曾经见过的唯一甚至是远程令人信服的论据,可以证明这样一个值的存在DataTable中,当传入值来创建新行时; null 表示“使用默认值”,DBNull 明确表示 null - 坦率地说,此 API 可以对这种情况进行特定处理 - 一个虚构的 DataRow。例如,DefaultValue 比引入无缘无故感染大量代码的 DBNull.Value 要好得多。

同样,ExecuteScalar 场景充其量是微不足道的;如果您正在执行标量方法,您期望结果。在没有行的情况下,返回 null 似乎并不太可怕。如果您绝对需要消除“无行”和“返回一个空值”之间的歧义,可以使用阅读器 API。

这艘船早已启航,现在修复它已经太晚了。但!请不要认为每个人都同意这是一件“显而易见”的事情。许多开发人员没有看到 BCL 上这个奇怪的问题的价值。

我实际上想知道这一切是否源于两件事:

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

摘要:

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

I'm going to disagree with the trend here. I'll go on record:

I do not agree that DBNull serves any useful purpose; it adds unnecessary confusion, while contributing virtually no value.

The argument is often put forward that null is an invalid reference, and that DBNull is a null object pattern; neither is true. For example:

int? x = null;

this is not an "invalid reference"; it is a null value. Indeed null means whatever you want it to mean, and frankly I have absolutely no problem working with values that may be null (indeed, even in SQL we need to correctly work with null - nothing changes here). Equally, the "null object pattern" only makes sense if you are actually treating it as an object in OOP terms, but if we have a value that can be "our value, or a DBNull" then it must be object, so we can't be doing anything useful with it.

There are so many bad things with DBNull:

  • it forces you to work with object, since only object can hold DBNull or another value
  • there is no real difference between "could be a value or DBNull" vs "could be a value or null"
  • the argument that it stems from 1.1 (pre-nullable-types) is meaningless; we could use null perfectly well in 1.1
  • most APIs have "is it null?" methods, for example DBDataReader.IsDBNull or DataRow.IsNull - neither of which actually require DBNull to exist in terms of the API
  • DBNull fails in terms of null-coalescing; value ?? defaultValue doesn't work if the value is DBNull
  • DBNull.Value can't be used in optional parameters, since it isn't a constant
  • the runtime semantics of DBNull are identical to the semantics of null; in particular, DBNull actually equals DBNull - so it does not do the job of representing the SQL semantic
  • it often forces value-type values to be boxed since it over-uses object
  • if you need to test for DBNull, you might as well have tested for just null
  • it causes huge problems for things like command-parameters, with a very silly behaviour that if a parameter has a null value it isn't sent... well, here's an idea: if you don't want a parameter sent - don't add it to the parameters collection
  • every ORM I can think of works perfectly well without any need or use of DBNull, except as an extra nuisance when talking to the ADO.NET code

The only even remotely compelling argument I've ever seen to justify the existence of such a value is in DataTable, when passing in values to create a new row; a null means "use the default", a DBNull is explicitly a null - frankly this API could have had a specific treatment for this case - an imaginary DataRow.DefaultValue for example would be much better than introducing a DBNull.Value that infects vast swathes of code for no reason.

Equally, the ExecuteScalar scenario is... tenuous at best; if you are executing a scalar method, you expect a result. In the scenario where there are no rows, returning null doesn't seem too terrible. If you absolutely need to disambiguate between "no rows" and "one single null returned", there's the reader API.

This ship has sailed long ago, and it is far far too late to fix it. But! Please do not think that everyone agrees that this is an "obvious" thing. Many developers do not see value in this odd wrinkle on the BCL.

I actually wonder if all of this stems from two things:

  • having to use the word Nothing instead of something involving "null" in VB
  • being able to us the if(value is DBNull) syntax which "looks just like SQL", rather than the oh-so-tricky if(value==null)

Summary:

Having 3 options (null, DBNull, or an actual value) is only useful if there is a genuine example where you need to disambiguate between 3 different cases. I have yet to see an occasion where I need to represent two different "null" states, so DBNull is entirely redundant given that null already exists and has much better language and runtime support.

回忆那么伤 2024-10-15 14:41:27

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

例如。如果您使用 ExecuteScalar(它返回结果集中第一行的第一列)并且您得到一个 null 返回,这意味着执行的 SQL 没有返回任何值。如果返回 DBNull,则意味着 SQL 返回了一个值,并且该值是 NULL。您需要能够区分其中的差异。

The point is that in certain situations there is a difference between a database value being null and a .NET Null.

For example. If you using ExecuteScalar (which returns the first column of the first row in the result set) and you get a null back that means that the SQL executed did not return any values. If you get DBNull back it means a value was returned by the SQL and it was NULL. You need to be able to tell the difference.

江心雾 2024-10-15 14:41:27

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

DbNull represents a box with no contents; null indicates the non-existence of the box.

少钕鈤記 2024-10-15 14:41:27

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

但我怀疑主要原因与 SQL Server 中参数默认值的工作方式和提供程序的实现有关。

要查看差异,请创建一个带有默认值参数的过程:

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 是否是个好主意,但很多人不知道我在这里提到的事情,所以我认为值得一提。

There are some differences between a CLR null and a DBNull. First, null in relational databases has different "equals" semantics: null is not equal to null. CLR null IS equal to null.

But I suspect the main reason is to do with the way parameter default values work in SQL server and the implementation of the provider.

To see the difference, create a procedure with a parameter that has a default value:

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

Well-structured DAL code should separate command creation from use (to enable using the same command many times, for example to invoke a stored procedure many times efficiently). Write a method that returns a SqlCommand representing the above procedure:

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

If you now invoke the command without setting the @s parameter, or set its value to (CLR) null, it will use the default value 'hello'. If on the other hand you set the parameter value to DBNull.Value, it will use that and echo DbNull.Value.

Since there's two different results using CLR null or database null as parameter value, you can't represent both cases with only one of them. If CLR null was to be the only one, it'd have to work the way DBNull.Value does today. One way to indicate to the provider "I want to use the default value" could then be to not declare the parameter at all (a parameter with a default value of course makes sense to describe as an "optional parameter"), but in a scenario where the command object is cached and reused this does lead to removing and re-adding the parameter.

I'm not sure if I think DBNull was a good idea or not, but a lot of people are unaware of the things I've mentioned here, so I figured it worth mentioning.

请爱~陌生人 2024-10-15 14:41:27

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

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

缺失数据对统计的影响:

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

You use DBNull for missing data. Null in the .NET language means that there is no pointer for an object/variable.

DBNull missing data: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx

The effects of missing data on statistics:

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

感受沵的脚步 2024-10-15 14:41:27

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

DBNull 在狭窄的用例中是必要的。否则,就不是。大多数人永远不需要 DBNull。我从来不允许它们进入我设计的数据存储中。我总是有一个值,因此我的数据永远不会是“”,我总是选择一个有意义的“默认值”类型,而且我从来不需要在代码中对所有内容进行两次这种荒谬的双重检查,一次 for 是对象 null ,在我将对象转换为实际数据类型(例如 Int 等)之前,再次确认是否为 DBNull。

在您可能需要的一种情况下,DBNull 是必要的...如果您使用某些 SQL 统计函数(例如:中值、平均值)..它们会特别对待 DBNull..去看看那些文档..某些函数不包含 DBNull在统计的总计数中...例如:(87 sum / 127 Total)与(87 sum / 117 Total)..区别在于其中 10 个列值是 DBNull...您可以看到这会发生变化统计结果。

我不需要用 DBNull 来设计我的数据库。如果我需要统计结果,我会明确地发明或添加一个列,例如“UserDidProvideValue”,用于需要某种特殊处理的一项,因为它不存在(例如,我的总计 117 将是标记的字段的总和) UserDidProvideValue=true) ...哈哈哈哈 - 在我的下辈子作为宇宙的统治者哈哈 - DBNull 永远不会被允许逃离 SQL 领域...整个编程世界现在有负担检查一切两次...当您是否曾经有过移动应用程序、桌面应用程序或网站需要有一个“空”整数? - 绝不...

To answer your question, you have to consider Why Does DBNull even exist?

DBNull is necessary in a narrow use case. Otherwise, it is not. Most people never need DBNull. I never allow them to be entered into data stores I design. I ALWAYS have a value, therefore my data is never "<null>", I always choose a type meaningful 'default value', and I never have to do this absurd double check everything twice in code, once for is the object null, and again for is it DBNull before I cast my object to my actual data type (such as Int, etc).

DBNull is necessary in one case you might need... if you use some of the SQL statistics functions (eg: median, average) .. they treat DBNull specially.. go look at those docs.. some functions do not include a DBNull in the total count for the statistic... eg: (87 sum / 127 total) vs. (87 sum / 117 total) .. the difference being that 10 of those column values were DBNull... you can see this would change the statistics result.

I have no need to design my databases with DBNull. If I ever needed statistical results, I would explicitly invent or add a column such as 'UserDidProvideValue' for that one item that needs some sort of special handling because it does not exist (eg my total of 117 would be the sum of the fields marked UserDidProvideValue=true) ... haha lol - in my next life as ruler of the Universe lol - DBNull would have never been allowed to escape the SQL realm... the entire programming world is now burdened to check everything twice... when have you ever had a mobile app or desktop app or website need to have a "null" integer? - Never...

自此以后,行同陌路 2024-10-15 14:41:27

我对这个问题没有一个好的答案,因为我问了类似的问题。

我之所以提出这个问题,是因为在将 DevEx 控件转换为 DateTime 类型以获取值并将其分配给我的属性后,我得到了 DBNull。

为了消除错误,我

_myRecord.MyProperty = (DateTime?)ControlNameDE.EditValue;

切换到

 _myRecord.MyProperty = ControlNameDE.EditValue as DateTime?;

I don't have a good answer to this question, because I was asking something similar.

I came to this question because I was getting a DBNull after casting a DevEx control to a DateTime type to get and assign the value to my property.

To smooth out the error I switched

from

_myRecord.MyProperty = (DateTime?)ControlNameDE.EditValue;

to

 _myRecord.MyProperty = ControlNameDE.EditValue as DateTime?;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文