强类型数据表/数据集中的可空类型 - 解决方法?

Posted

技术标签:

【中文标题】强类型数据表/数据集中的可空类型 - 解决方法?【英文标题】:Nullable types in strongly-typed datatables/datasets - workarounds? 【发布时间】:2011-03-22 23:44:55 【问题描述】:

强类型数据表支持“可空”字段类型,但设计器不允许您将任何值类型字段的设置更改为“允许空值”。 (即:字符串类型允许为空,但 int 不允许)。

解决方法是在您想要获取 Myfield 的任何时候调用 IsMyFieldNull()。如果您在 MyField 确实包含 null 时访问它,则会引发一个错误。

这是一个非常令人头疼的问题,此外还会在 null 出现时导致许多运行时错误,从而导致您的应用程序崩溃。多年来,我一直向微软抱怨这一点,但 Visual Studio 的每个新版本仍然不允许使用可为空的值类型。

我的问题:有人知道可以用来解决这个主要缺点的奇特扩展方法吗?

【问题讨论】:

您可以在DataSet的.Designer文件中修改表的行属性定义,但似乎每次通过VS设计器修改DataSet时都会重新创建它,即使您不触摸具体表。 【参考方案1】:

我只需要为此找到解决方法。我需要更改为 ASP.NET 2.0 编写的网页的一些旧代码。该页面使用 Telerik RadMenu,一个菜单控件。此组件要求根元素具有正确的 DBNull 值(对于 parentID)。 所以,当我编译旧代码时,RadMenu 组件给了我很多问题。首先是关于约束的例外,然后它不知道哪些元素是根元素,整个事情看起来很糟糕。

但我解决了它,这对我有用。

在 Table Adapter 设计器的 ParentID 列的 Properties 页面中,我使用了: - AllowDBNull:真 - DefaultValue:-1(-1 是该列不正常出现的值) NullValue 属性停留在“抛出异常”,因为我无法更改。

在使用来自表适配器的值的代码中,我使用了这个构造(VB.NET 代码,而不是 C#,因为这个问题被标记):

Dim MenuBarTable As DAL.Page.MenuBarDataTable 'The Table Adapter
MenuBarTable = PageObj.GetMenuBar()  'The generated Get function 
MenuBarTable.ParentIDColumn.AllowDBNull = True 

    For Each row As Page.MenuBarRow In MenuBarTable.Rows
        If row.IsParentIDNull() Then 
            row.SetParentIDNull() 
        End If
    Next

为表适配器生成的代码为应该允许 DBNULL 的每一列生成两个函数。它们本来是在处理 NULL 时使用的,但它是 Microsoft 的一个笨拙的解决方案。幕后发生的事情是表适配器将从 Get 函数输出列的 DefaultValue 而不是 NULL。我称之为“模拟 NULL”或“假 NULL”。

函数 IsParentIDNull() 将实际检查该行是否包含此“假 NULL”,例如该列的 DefaultValue,当它出现时,我使用 SetParentIDNull() 函数插入一个正确的 DBNull。

这个解决方案对我有用,但不是很优雅,也不是很有效,但我希望它可以对其他人有所帮助。

【讨论】:

【参考方案2】:

你可以这样做: 如果未设置,则将 AllowDbNull 设置为 true; DefaultValue 保持开启; NullValue 保持开启(抛出异常)。然后从代码中,当您想将列设置为空时,您可以使用内置方法 Set_Column_Null()。 看我的例子:

if (entry.PosX.HasValue)
    newRow.PosX = entry.PosX.Value;
else
    newRow.SetPosXNull(); 

【讨论】:

【参考方案3】:

如果您使用的是 .Net 3.5 或更高版本,这些扩展可能对您有用: http://msdn.microsoft.com/en-us/library/system.data.datarowextensions.field.aspx

根据文档,它完全支持可空类型。它允许您使用像

这样的结构
MyRow.Field<int?>("MyField") 

如果您将其分配给或从正确类型的现有变量中分配,编译器可以自动推断类型,您可以省略类型说明符,使其短至

int? myVar = MyRow.Field("MyField");
MyRow.Field("MyField") = myVar;

仍然不完美,但比在任何地方都必须使用 IsMyFieldNull() 等更易读。

哦,如果您想在列名拼写错误方面更加安全,您可以使用类似

MyRow.Field(MyTable.MyFieldColumn)

不要忘记添加对 System.Data.DataSetExtensions 的引用

【讨论】:

我一直在使用它,它有帮助,但它不是强类型的。我正在寻找允许可空字段类型(对于值类型)的强类型数据表 我同意。我也同意,如果他们在即将发布的 Visual Studio 版本中包含一些内容,那就太好了。这是我使用当前工具得到的最接近的结果。我一直在研究 T4 模板,但对于它的价值(对我而言)来说,这看起来工作量太大。 是的,很难证明重建整个数据集生成器工具只是为了添加一个功能。 (是的,我也调查过;) 刚刚遇到这个...方便的扩展,这不是强类型的吗?据我所知,最后一种方法(使用强类型列参数)完全避免了魔术字符串。 像 boomhauer 和 M-Peror 一样,我也很想推出自己的数据集生成器,但也因成本效益比而推迟。使用上面介绍的最后一种方法似乎对我来说是一个糟糕的情况。【参考方案4】:

我不知道为什么var x = !IsMyFieldNull() ? MyField : null(或类似的)如此令人头疼。

我想您可以在 SqlDataReader 周围编写一个包装器,以便在您将数据读入 DataTable 时以某种方式捕获这些空值,或者您可以为您的查询编写相当于 .TryParse() 的内容:一些不错的封装,例如:

var x = myDataTable.TryParse(myField);

.TryParse 是一个扩展方法,类似于:

public static TryParse(DataRow myField)

    if(!myField == DbNull) //Or similar
       return myField.Value;

如您所见,这基本上是粗略的伪代码,但您明白了。编写一次代码,然后将其作为扩展方法调用。

【讨论】:

艾伦,问题在于现实世界场景的冗长: var x = MyDataTable.IsMyfieldNull()?(int?)null: (int?)MyDataTable.MyField;现在对 20 个表重复 5 次,每个表有 20 列。不好玩;)哦,别忘了分配所需的代码..【参考方案5】:

.net 3.0 中引入了可空类型,这些可以与数据集一起使用。您像这样声明一个可为空的 int int? myNullableInt = null 这是 MSDN 文章的链接:Nullable Types C#

就个人而言,我首先会避开数据库中的空值(如果您有这样做的奢侈)。 NULL 确实允许“未定义”或“未知”状态。很少有这个问题,例如包含姓氏的字符串字段通常会设置为可空,而默认为“”会是一个更好的选择。将空值放入数据库会使事情变得不必要地困难Null Values In Databases,此外它会将空值传播到代码中,您必须努力避免空值引用异常。

不幸的是,互联网上有很多关于 DB 的坏东西(例如过度使用 null 就可以了)。这些观点通常是由那些真正不了解它们背​​后的理论的人提出的,另一个典型的例子是没有关系的数据库,“因为在代码中处理这些更灵活/更快”。这意味着开发人员必须重写现有的 [数据库层] 功能,数据库不可避免地确实可以更有效地处理这些功能并且具有更高的可靠性。我说不可避免,当然,进行重写的开发人员正在重新实现甲骨文/微软/谁有大型团队花费大量时间优化等的东西。然而,你经常看到有人提倡这一点一个设计。这家伙真的很懂数据库,DBDebunkings 他花了很多时间试图揭穿很多让关系数据库脱离其理论根源的无稽之谈。

【讨论】:

我没有要求关于 null 的邪恶的布道。听了1000遍。我正在寻找一种简单的方法来使可空对象与强类型数据集一起使用。也许是扩展方法或类似方法。而且,可空值是在 .net 2 中引入的,而不是 3。 Keith,不是故意粗鲁,只是你回答了一个我没有问的问题。 嗯,我很确定他回答了。 C# 中有可以为空的值类型。他链接了参考并给了你一个例子。因此,使用可空类型来检索您的数据,并可能构建扩展方法或静态函数来处理可空值和不可空值类型之间的转换,以便您可以将其传递给您可能无法控制的其他模块。 Anton- 我知道什么是可空类型。我的问题是关于强制强类型数据集通过扩展方法来支持它们,或者一些同样“聪明”的方法来解决它们缺乏对它们的支持。即,没有在每个数据表上的每个字段周围编写手动编码的包装器等. 有意义吗? 不接受可空类型作为 DataColumns 的类型,不管是否讲道。【参考方案6】:

我同意允许可空类型是一个不错的功能。

如果您将列上的“NullValue”属性设置为“-1”而不是“(引发异常)”,则该属性将在该列为空时返回 -1 而不是引发异常。您仍然必须将列设置为允许空值。

或者,您始终可以将数据类型设置为“System.Object”,并允许 nulls=true。您可以访问列的值,而无需使用“IsMyFieldNull()”方法。当该列为空时,该列的值将为“System.DbNull.Value”。如果您不喜欢使用“System.DbNull.Value”,可以将“NullValue”属性设置为“(Nothing)”而不是“(Throw exception)”,然后将该对象与空引用进行比较。

【讨论】:

-1 是数据库中的有效值,因此不能使用它。但请记住,我也在谈论所有值类型。日期时间、双打等。 您也可以按照标准使用值类型的最大值来表示空值。对于一个字节,它是 255,对于一个 int32,它是 2,147,483,647 (int32.MaxValue)。请注意,日期值的最大值与您对数据库的期望值不同。 Date.MaxValue 不等于最大的 SQL DateTime 或 SmallDateTime。一般来说,值类型的最大值在实践中从不使用,而在字节的情况下,经常使用最小值(0)。 Carter,有很多变通方法,但在可空类型时代,没有一种是可以接受的。 ;)【参考方案7】:

在 VS 2008 中,您只需在 nullvalue 属性中输入“0”即可。 如果您使用的是 vs2005,则必须使用 XML 编辑器进行操作。 您必须将msprop:nullValue="0" 作为属性添加到该列。

【讨论】:

问题是,一个“0”是一个合法的值..我真正需要(或想要)的是null value = null,数据类型是int?而不是 int。

以上是关于强类型数据表/数据集中的可空类型 - 解决方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用 C# 8 的可空引用类型

#5 kotlin nullable 可空类型

删除强类型数据集中的列时出错?

以编程方式更改 .net 强类型数据集中的表名

c# 中的可空类型是啥?

C# 8中的可空引用类型