DataTableにユニークキーを設定する

        // なんらかのデータ検索
        // 対象テーブルのキーはユニークキー(つまりキーにNull値を含む)だったとする
        DataTable t = Find (10);
        // DataTable.Mergeを使いたいのでPrimaryKeyを設定するが・・・
        // このタイミングでプライマリキーに指定したカラムのAllowDBNullがfalseになり例外発生
        t.PrimaryKey = new[] { t.Columns[0],t.Columns[1] };
        foreach (DataColumn c in t.Columns)
            c.AllowDBNull = true;

上記のようにDataTableを返すメソッドがあったとします。検索対象となるテーブルのキーはユニークキーだったり、検索結果が複数のテーブルをリンクして取得したものでキーがされていなかったとします。DataTable.Mergeが便利なのでPrimaryKeyを設定した後、カラムのAllowDBNullをfalseにしようとしても既にデータが格納済みのDataTableでは、制約に引っかかってしまい、PrimaryKeyの設定がうまくいきません。案として、

  1. 検索メソッド(Find)を修正する
  2. キーを設定したDataTableを用意し、読み込ませる

が、考えられます。案1が可能ならそれに越したことはありませんが、メソッドは社外で作られて手出しができない場合は案2となりますが、データ量が大きければパフォーマンス的に望ましくありません。

どうしても案2が嫌だったので10分くらい考えて、以下のような逃げ手を思いつきました。

        // 別にデータをロードする訳ではないのですが・・・
        // BeginLoadDataを呼ぶとその間、制約が無効になるという副作用を利用
        t.BeginLoadData ();
        t.PrimaryKey = new[] { t.Columns[0],t.Columns[1] };
        foreach (DataColumn c in t.Columns)
            c.AllowDBNull = true;
        t.EndLoadData ();

なんか副作用を利用しているみたいで気分的にはよろしくありませんが、パフォーマンスの方が大事だったので。(^^;

ただ、個人的にはDataTable.PrimaryKeyの仕様はおかしいんじゃないかと思います。
PrimaryKeyへ代入すると指定したカラムのAllowDBNullは無条件にfalseになるのですが、その後、AllowDBNullをtrueに書き換えられるんですよね。だとすると、falseにするのは余計な御世話です。そもそも、統一的な動作を考えると、

  • PrimaryKeyにカラムを設定してもAllowDBNullは変更しない
  • PrimaryKeyに設定したカラムのAllowDBNullはtrueにはできない

この何れかの動作になるべきだと思うんですよね。