0

正如@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 后面的所有字段。

必须有一种方法可以做到这一点,而无需在我的代码中进行几十次笨拙的检查。那么,使用 DBNulls 的行的魔术是什么?

4

2 回答 2

0

在从服务器检索它们之前删除 DbNull 值,方法是修改要调用的 SQL 选择查询IsNull(或在 Access 后端的情况下Nz)。定义在这里

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

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

于 2012-08-26T15:12:52.740 回答
0

写一个扩展:

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
于 2012-08-26T13:43:52.280 回答