如何处理所有 DBNull 而无需每次都检查它们(使用生成的 DataSet)? [复制]

Posted

技术标签:

【中文标题】如何处理所有 DBNull 而无需每次都检查它们(使用生成的 DataSet)? [复制]【英文标题】:How to handle all DBNulls without checking for them every time (with a generated DataSet)? [duplicate] 【发布时间】:2012-08-26 13:08:18 【问题描述】:

正如@nicholas 正确指出的那样,我最初的问题是有点误导,因为这个案例有点具体。

所以这是真正的问题:

我使用 DataGridView 控件和从生产数据库中的表生成的 DataSource 创建了 Windows 窗体应用程序。

我的程序的目标是查看表,可选择编辑注释列并将表数据导出到 XML 文件,该模式是严格定义的,使用 XSD 和其他我无法更改的独立约束。

如果某些值未定义,则不得将某些字段插入到 XML 输出文件中,这一点尤为重要。

明确表示我不能为这些字段插入空标签。这就是我需要空值的原因。仅在给定值的位置插入 XML 标记。这就是为什么我需要 DBNull 值,而不是任何其他默认值。

生成的 DataSet 包含在访问 DBNull 值时引发异常的 getter。我不能更改生成的代码,这是一条规则,你不能更改生成的代码,尤其是可能(并且将会)被覆盖的代码。

我通过用“try/catch”块包围每个可空字段的分配解决了这个问题。如果生成的 getter 引发异常,我的 XML 对象的可为空字段将保持其默认的空值,这是我的预期行为。

稍后 - 我的 XML 构造函数将在生成 XML 输出时跳过这些字段。

但仍然存在一个问题:为什么它必须如此丑陋?如果我有一百个可空字段怎么办?我必须写一百个“try/catch”块吗?对我来说,这似乎是整个 Microsoft 机制中的一个错误。或者也许有一个简单而优雅的解决方案?

-- 这是原始内容,以了解第一个答案的来源:

请,我知道我可以检查 DBNull 的每个字段,但必须 成为程序员的一种自动方式。这将是最愚蠢的事情 在整个 .NET/C# 中 - 必须编写“if”或“try/catch”语句 我巨大的桌子的每一列!我试图在我的桌子上使用“foreach” 行类型,但它没有工作。异常被抛出 “foreach”主体的开头,似乎我什至尝试触摸 DBNull, 抛出异常。如果我把整个代码块放在里面 “try/catch” - 它不起作用,因为它会跳过后面的所有字段 第一个 DBNull。

必须有一种方法可以做到这一点,而不需要把几十个 fugly 检查我的代码。那么,行的魔术是什么 DBNull?

【问题讨论】:

写一个函数来提取值,如果是DBNull则为默认值,当需要读取一列时使用它 this answer 可能会有所帮助。 一行代码;看这里:***.com/questions/2433155/handle-dbnull-in-c-sharp 我忘了提:有一个生成的 getter 会引发异常 - 无法触及 DBNull 值。并且没有办法更改该 getter - 当然我可以更改它(对于每个 NULL 列),但每次更改相关对象都会覆盖它。亲爱的微软,你为什么要这么做?我认为 NULL 是 TSQL 中的一个正常值,而不是“总是抛出一个愚蠢的无意义的异常”。我不需要默认值! 【参考方案1】:

写一个扩展:

public static T GetValue<T>(this IDataRecord @this, string name, T defaultValue = default(T))

    int ordinal = @this.GetOrdinal(name));
    return @this.GetValue<T>(ordinal, defaultValue);
 

public static T GetValue<T>(this IDataRecord @this, int ordinal, T defaultValue = default(T))

    return @this.IsDBNull(ordinal) ? defaultValue : (T)@this.GetValue(ordinal);
 

像这样使用它:

connstr = @"Data Source=.\sqlexpress;Integrated Security=SSPI;Initial Catalog=Test;";

string sql = "select WidgetId, WidgetName, WidgetValue, CreatedDt, ModifiedDt from dbo.widgets";

using (connection = new SqlConnection(connstr))

    connection.Open();
    using (command = new SqlCommand(sql, connection))
    using (IDataReader reader = command.ExecuteReader())
    
        while (reader.Read())
        
            Console.Write(reader.GetValue<int>("WidgetId").ToString());
            Console.Write("\t");
            Console.Write(reader.GetValue<string>("WidgetName"));
            Console.Write("\t");
            Console.Write(reader.GetValue<int>("WidgetValue"));
            Console.Write("\t");
            Console.Write(reader.GetValue<DateTime>("CreatedDt"));
            Console.Write("\t");
            Console.Write(reader.GetValue<DateTime>("ModifiedDt"));
            Console.Write("\n");
        
    

结果:

1       Widget 1        0       7/17/2012 6:13:26 AM    1/1/0001 12:00:00 AM
2       Widget 2        0       7/17/2012 6:13:26 AM    1/1/0001 12:00:00 AM
3       Widget 3        0       7/17/2012 6:13:26 AM    1/1/0001 12:00:00 AM

【讨论】:

【参考方案2】:

通过修改 SQL 选择查询以调用 IsNull(或在 Access 后端 Nz 的情况下)从服务器检索它们之前删除 DbNull 值。 Definition here.

string sql = "select WidgetId, IsNull(WidgetName,""), " +
             "IsNull(WidgetValue,0), CreatedDt, ModifiedDt from dbo.widgets";

假设您有某种适当的默认值或使用哨兵值稍后捕获空值。

【讨论】:

我需要空值。 Null 表示我没有将标签放入输出 XML、“”和 0 以及类似的意思是我需要放入一个空标签或带有“0”内容的标签。 XML 模式和规则不是我定义的,所以我必须为此做一个非常肮脏的解决方法。空值只是整个工作所必需的。 @Harry,我建议您修改您的原始问题,以反映程序的这些额外限制/所需结果(包括您对“生成的 getter”的评论) 你绝对是对的。我认为我的问题有点误导。

以上是关于如何处理所有 DBNull 而无需每次都检查它们(使用生成的 DataSet)? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

从 DataAdapter 拉数据时如何处理 DBNull

HikariCP 如何处理不完整的 JDBC 事务?

使用 HttpClient.GetFromJsonAsync(),如何处理基于 HttpStatusCode 的 HttpRequestException 而无需额外的 SendAsync 调用?

如何处理所有的贬低

如何处理来自大型 ResultSet 的数据而不将它们全部加载到内存中?

Xamarin 表单:如何处理 flowlistview 所选项目的可见性?