Visual Studio には、型指定された DataSet + TableAdapter をウィザードベースで自動生成する機能があります。さらに、Visual Studio 2008 以降では、階層更新を実現する TableAdapterManager クラスが追加で自動生成されるようになりました。
今さらながらですが、TableAdapterManager について調べて、いろいろ発見があったので忘れないように書いておきます。
階層更新とは、簡単に言うと、複数のテーブルで構成される DataSet が持つ更新データを、データベースの整合性に関する規則(参照整合性規則)を守って、データベースに挿入、更新、削除するプロセスのことです。(詳しい説明は MSDN ライブラリの 階層更新 を参照してください)
例として、上の画像のような、Northwind サンプルデータベースの Customers と Orders テーブル両方を同時に管理するアプリケーションを考えます。(Customers テーブルは CustomerID を主キーとして持ち、Orders テーブルは CustomerID を外部キーとして持っています。)
新しい顧客から注文があった場合は、先に新しい顧客レコードを Customers テーブルに Insert してから、注文レコードを Orders テーブルに Insert しなければなりません。
Customers テーブルから顧客レコードを Delete する場合は、先に Orders テーブルの当該顧客の注文レコードをすべて Delete してからにしなければなりません。
さらに、Insert、Update、Delete の順序も重要です。例えば、Customers テーブルの既存のレコードの主キー値を変更する場合、以下のような手順にしなければなりません。
-
Customers テーブルに、新しい CustomerID 値で、新しいレコードを Insert
-
Orders テーブルの当該レコードの CustomerID を新しい値に Update
-
Customers テーブルの旧 CustomerID のレコードを Delete
従って、Insert => Update => Delete の順で行う必要があります(TableAdapterManager のデフォルトがこれ。他に、Update => Insert => Delete とすることも可能)。
Visual Studio 2005 以前のバージョンでは、上記のような参照整合性規則を守って更新を行う(即ち、階層更新を実現する)ためのコードを自力で実装する必要がありました。具体的には以下ようなコードを実装しなければなりません。
-
まず、DataTable の Select メソッドを使用して特定の RowState(Added、ModifiedCurrent、Deleted)を持つ行だけを参照する DataRow 配列を取得します。(DataAdapter によるデータ ソースの更新 の「挿入、更新、削除の順序」のセクション参照)
-
その後、各テーブルの TableAdapter の Update(DataRow[]) メソッドを適切な順序で呼び出し、各テーブルから RowState 別に取得した DataRow 配列を適切な順序で渡して処理します。
-
当然ながらトランザクション処理も必要です。
Visual Studio 2008 から新しく追加された TableAdapterManager クラスには、階層更新を実現するロジックを持つ UpdateAll メソッドが実装されています。MS-DTC を使わない手動トランザクション処理も実装されています。
実際に、MSDN ライブラリのチュートリアル SQL Server Express データベースの作成 と SQL Server Express データベース内のデータへの接続 (Windows フォーム) を参考に、型指定された DataSet + TableAdapter を作って、その中の TableAdapterManager のコードを見てみました。(もっと詳しいチュートリアルがありました。下の 2011/12/22 追記の「その1」を参照ください)
TableAdapterManager の UpdateAll メソッドは、Customers => Orders の順で両方のテーブルに対して Insert を行い、次に Customers => Orders の順で両方のテーブルに対して Update を行い、そして、最後に Orders => Customers の順で両方のテーブルに対して Delete を行うというように、上に述べた参照整合性規則を守って更新を実行するためのコードが実装されているのが確認できました。
TableAdapterManager は万能ではないと思いますが、多くのケースで、自力で階層更新を実現するためのコードを書く必要がなくなったのではないでしょうか。
自動生成されたコードで対応できない場合は、TableAdapterManager クラスは partial として定義されているので、自力でコードを書いてそれを拡張すれば、Visual Studio 2005 以前のときと比べて比較的簡単に対応可能と思います。
------------- 2011/12/22 追記 -------------
その1
Customers テーブルと Orders テーブルを使ったアプリケーションの作成は、上記のページより、10 行でズバリ !! 非接続型のデータ アクセス (ADO.NET) (C#) の方が詳しく書いてあって、参考になると思います。
その2
単一テーブルのアプリケーションにおいて、既存のレコードを Delete して、そのレコードと同じ主キーを持つレコードを新たに Insert する場合、Delete => Insert の順でないとうまくいきません。
TableAdapterManager は、Insert => Update => Delete(デフォルト)または Update => Insert => Delete のいずれかしか更新順序は選べませんので、上記のケースには対応できないということになります。
本文に述べた Customers テーブルの既存のレコードの主キー値を変更するケースより、上記のケースの方が多そうな気がするのですが、Microsoft は何故 Delete を最初にしなかったのでしょう? 気になります。(2015/11/12 追記: Delete => Insert などという乱暴なことを許すと、ユーザーのミスで問題が出る可能性が高くなるからではないかと最近思い初めています。Update すれば済む話ですから)