0

@nicholasが正しく指摘したように、私の最初の質問は少し誤解を招くものでした。このケースは少し具体的だからです。

だからここに本当の問題があります:

DataGridView コントロールと、運用データベースのテーブルから生成された DataSource を使用して、Windows フォーム アプリケーションを作成しました。

私のプログラムの目標は、テーブルを表示し、オプションでコメント列を編集し、テーブル データを XML ファイルにエクスポートすることです。このスキーマは、XSD と変更できない他の独立した制約の両方で厳密に定義されています。

一部の値が定義されていない場合、一部のフィールドを XML 出力ファイルに挿入してはならないことが特に重要です。

これらのフィールドに空のタグを挿入してはいけないと明示的に言われています。それが、null 値が必要な理由です。値が指定されている場所にのみ XML タグを挿入します。そのため、他のデフォルト値ではなく、DBNull 値が必要です。

生成された DataSet には、DBNull 値へのアクセス時に例外をスローするゲッターが含まれています。生成されたコードを変更することはできません。ルールです。生成されたコード、特に上書きされる可能性がある (そして上書きされる可能性がある) コードは変更しないでください。

null 許容フィールドの各割り当てを「try/catch」ブロックで囲むことで、この問題を解決しました。生成された getter が例外をスローした場合、XML オブジェクトの nullable フィールドはデフォルトの null 値を保持します。これは私の予想される動作です。

後で - 私の XML コンストラクターは、XML 出力の生成中にこれらのフィールドをスキップします。

しかし、1 つの問題が残っています。100 個の null 許容フィールドがある場合はどうなりますか? 100 個の "try/catch" ブロックを作成する必要がありますか? 私には、Microsoft メカニズム全体のバグのように思えます。それとも、これに対するシンプルでエレガントな解決策がありますか?

-- 最初の回答がどこから来たのかを理解するための元のコンテンツは次のとおりです。

各フィールドで DBNull をチェックできることはわかっていますが、プログラマー向けの自動化された方法が必要です。巨大なテーブルの各列に「if」または「try/catch」ステートメントを書かなければならないのは、.NET/C# 全体で最も愚かなことです。テーブルの行タイプで「foreach」を使用しようとしましたが、うまくいきませんでした。「foreach」本体の一番最初に例外がスローされ、DBNullに触れようとしても例外がスローされるようです。コード ブロック全体を「try/catch」内に配置すると、最初の DBNull の後ろのすべてのフィールドがスキップされるため、機能しません。

私のコードに数十の厄介なチェックを入れずにそれを行う方法があるに違いありません。では、DBNull を使用した行の魔法のトリックは何ですか?

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 に答える