注
DataSet クラスと関連クラスは、アプリケーションがデータベースから切断されている間にアプリケーションがメモリ内のデータを操作できるようにする、2000 年代初頭のレガシ .NET Framework テクノロジです。 このテクノロジは、ユーザーがデータを変更し、変更をデータベースに保持できるアプリに特に役立ちます。 データセットは実証済みの成功したテクノロジですが、新しい .NET アプリケーションには Entity Framework Core を使用することをお勧めします。 Entity Framework は、オブジェクト モデルとして表形式データを操作するより自然な方法を提供し、よりシンプルなプログラミング インターフェイスを備えています。
データセットは、データのメモリ内コピーです。 そのデータを変更する場合は、それらの変更をデータベースに保存することをお勧めします。 これは、次の 3 つの方法のいずれかで行います。
TableAdapter の
Update
メソッドのいずれかを呼び出すTableAdapter の
DBDirect
メソッドのいずれかを呼び出すことによってデータセットにデータセット内の他のテーブルに関連するテーブルが含まれている場合に Visual Studio によって生成される TableAdapterManager で
UpdateAll
メソッドを呼び出す
データセット テーブルを Windows フォームまたは XAML ページ上のコントロールにデータバインドする場合、データ バインディング アーキテクチャはすべての処理を行います。
TableAdapters に慣れている場合は、次のいずれかのトピックに直接進むことができます。
トピック | 説明 |
---|---|
データベースに新しいレコードを挿入する | TableAdapters オブジェクトまたは Command オブジェクトを使用して更新と挿入を実行する方法 |
TableAdapter を使用してデータを更新する | TableAdapters を使用して更新を実行する方法 |
階層更新 | 2 つ以上の関連テーブルを含むデータセットから更新を実行する方法 |
コンカレンシー例外を処理する | 2 人のユーザーがデータベース内の同じデータを同時に変更しようとしたときに例外を処理する方法 |
方法: トランザクションを使用してデータを保存する | システムを使用してトランザクションにデータを保存する方法。 Transactions 名前空間と TransactionScope オブジェクト |
トランザクションにデータを保存する | トランザクション内のデータベースにデータを保存する方法を示す Windows フォーム アプリケーションを作成するチュートリアル |
データベースにデータを保存する (複数のテーブル) | レコードを編集し、複数のテーブルの変更をデータベースに保存する方法 |
オブジェクトからデータベースにデータを保存する | TableAdapter DbDirect メソッドを使用して、データセット内にないオブジェクトからデータベースにデータを渡す方法 |
TableAdapter DBDirect メソッドを使用してデータを保存する | TableAdapter を使用して SQL クエリをデータベースに直接送信する方法 |
データセットを XML として保存する | データセットを XML ドキュメントに保存する方法 |
2 段階の更新
データ ソースの更新は 2 段階のプロセスです。 最初の手順では、データセットを新しいレコード、変更されたレコード、または削除されたレコードで更新します。 アプリケーションがそれらの変更をデータ ソースに送り返さない場合は、更新が完了します。
変更をデータベースに送り返す場合は、2 番目の手順が必要です。 データ バインド コントロールを使用していない場合は、データセットの設定に使用したのと同じ TableAdapter (またはデータ アダプター) の Update
メソッドを手動で呼び出す必要があります。 ただし、異なるアダプターを使用して、データ ソース間でデータを移動したり、複数のデータ ソースを更新したりすることもできます。 データ バインディングを使用せず、関連するテーブルの変更を保存する場合は、自動生成された TableAdapterManager
クラスの変数を手動でインスタンス化し、その UpdateAll
メソッドを呼び出す必要があります。
データセットには、行のコレクションを含むテーブルのコレクションが含まれています。 基になるデータ ソースを後で更新する場合は、行を追加または削除するときに、 DataTable.DataRowCollection
プロパティのメソッドを使用する必要があります。 これらのメソッドは、データ ソースの更新に必要な変更の追跡を実行します。 Rows プロパティで RemoveAt
コレクションを呼び出した場合、削除はデータベースに伝達されません。
データセットをマージする
データセットを別のデータセットとマージすることで、データセットの内容 を 更新できます。 これには、 ソース データセットの内容を呼び出し元のデータセット ( ターゲット データセットと呼ばれます) にコピーすることが含まれます。 データセットをマージすると、ソース データセット内の新しいレコードがターゲット データセットに追加されます。 さらに、ソース データセット内の追加の列がターゲット データセットに追加されます。 データセットのマージは、ローカル データセットがあり、別のアプリケーションから 2 つ目のデータセットを取得する場合に便利です。 また、XML Web サービスなどのコンポーネントから 2 つ目のデータセットを取得する場合や、複数のデータセットのデータを統合する必要がある場合にも便利です。
データセットをマージするときに、ターゲット データセットに既存の変更を保持するかどうかをMergeメソッドに指示するブール引数 (preserveChanges
) を渡すことができます。 データセットは複数のバージョンのレコードを保持するため、複数のバージョンのレコードがマージされることに注意することが重要です。 次の表は、2 つのデータセット内のレコードがどのようにマージされるかを示しています。
DataRowVersion | ターゲット データセット | ソース データセット |
---|---|---|
元の画像サイズ | James Wilson | James C. Wilson |
電流 | Jim Wilson | James C. Wilson |
前のテーブルで Merge メソッドを preserveChanges=false targetDataset.Merge(sourceDataset)
呼び出すと、次のデータが返されます。
DataRowVersion | ターゲット データセット | ソース データセット |
---|---|---|
元の画像サイズ | James C. Wilson | James C. Wilson |
電流 | James C. Wilson | James C. Wilson |
preserveChanges = true targetDataset.Merge(sourceDataset, true)
を使用して Merge メソッドを呼び出すと、次のデータが得られます。
DataRowVersion | ターゲット データセット | ソース データセット |
---|---|---|
元の画像サイズ | James C. Wilson | James C. Wilson |
電流 | Jim Wilson | James C. Wilson |
注意事項
preserveChanges = true
シナリオでは、ターゲット データセット内のレコードに対して RejectChanges メソッドが呼び出されると、ソース データセットの元のデータに戻ります。 つまり、元のデータ ソースをターゲット データセットで更新しようとすると、更新する元の行が見つからない可能性があります。 別のデータセットにデータ ソースの更新されたレコードを入力し、マージを実行してコンカレンシー違反を防ぐことで、コンカレンシー違反を防ぐことができます。 (コンカレンシー違反は、データセットの入力後に別のユーザーがデータ ソース内のレコードを変更したときに発生します)。
制約を更新する
既存のデータ行を変更するには、個々の列のデータを追加または更新します。 データセットに制約 (外部キーや null 非許容制約など) が含まれている場合は、更新時にレコードが一時的にエラー状態になる可能性があります。 つまり、1 つの列の更新が完了した後、次の列に移動する前にエラー状態になる可能性があります。
早期制約違反を防ぐために、更新制約を一時的に中断できます。 これは、次の 2 つの目的に役立ちます。
1つの列の更新を完了し、別の列の更新を開始していない場合に、エラーが発生しないようにします。
特定の更新イベント (検証によく使用されるイベント) が発生するのを防ぎます。
注
Windows フォームでは、データ グリッドに組み込まれているデータ バインディング アーキテクチャは、フォーカスが行から移動するまで制約チェックを中断し、 BeginEdit、 EndEdit、または CancelEdit メソッドを明示的に呼び出す必要はありません。
データセットに対して Merge メソッドが呼び出されると、制約は自動的に無効になります。 マージが完了すると、有効にできないデータセットに制約がある場合は、 ConstraintException がスローされます。 この場合、 EnforceConstraints プロパティは false,
に設定され、 EnforceConstraints プロパティを true
にリセットする前に、すべての制約違反を解決する必要があります。
更新が完了したら、制約チェックを再び有効にできます。制約チェックは、更新イベントを再度有効にして発生させることもできます。
イベントの中断の詳細については、「データセットの 入力中に制約を無効にする」を参照してください。
データセットの更新エラー
データセット内のレコードを更新すると、エラーが発生する可能性があります。 たとえば、誤った型のデータを誤って列に書き込んだり、長すぎるデータや、他の整合性の問題があるデータを書き込んだりすることがあります。 または、更新イベントの任意の段階でカスタム エラーが発生する可能性がある、アプリケーション固有の検証チェックがある場合もあります。 詳細については、「 データセット内のデータを検証する」を参照してください。
変更に関する情報を保持する
データセットの変更に関する情報は、変更されたことを示す行にフラグを設定する (RowState)、レコードの複数のコピーを保持する (DataRowVersion) という 2 つの方法で保持されます。 この情報を使用することで、プロセスはデータセット内で何が変更されたかを判断し、適切な更新をデータ ソースに送信できます。
RowState プロパティ
DataRow オブジェクトの RowState プロパティは、特定のデータ行の状態に関する情報を提供する値です。
次の表では、 DataRowState 列挙体の使用可能な値について詳しく説明します。
DataRowState 列挙定数の値 | 説明 |
---|---|
Added | 行が項目として DataRowCollectionに追加されました。 (この状態の行は、最後の AcceptChanges メソッドが呼び出されたときに存在しなかったため、対応する元のバージョンを持っていません)。 |
Deleted | 行は、DataRow オブジェクトのDeleteを使用して削除されました。 |
Detached | 行は作成されましたが、 DataRowCollectionの一部ではありません。 DataRow オブジェクトは、作成された直後、コレクションに追加される前、およびコレクションから削除された後に、この状態になります。 |
Modified | 行の列の値が何らかの方法で変更されました。 |
Unchanged | AcceptChangesが最後に呼び出されてから、行は変更されていません。 |
DataRowVersion 列挙型
データセットは複数のバージョンのレコードを保持します。 DataRowVersion フィールドは、DataRow オブジェクトの Item[] プロパティまたは GetChildRows メソッドを使用して、DataRowで見つかった値を取得するときに使用されます。
次の表では、 DataRowVersion 列挙体の使用可能な値について詳しく説明します。
DataRowVersion 列挙定数の値 | 説明 |
---|---|
Current | レコードの現在のバージョンには、最後に AcceptChanges が呼び出されてからレコードに対して実行されたすべての変更が含まれています。 行が削除されている場合、現在のバージョンはありません。 |
Default | データセット スキーマまたはデータ ソースで定義されているレコードの既定値。 |
Original | レコードの元のバージョンは、データセットで最後に変更がコミットされた時点のレコードのコピーです。 実際には、これは通常、データ ソースから読み取られるレコードのバージョンです。 |
Proposed | 更新中に一時的に使用できるレコードの提案されたバージョン。つまり、 BeginEdit メソッドを呼び出してから EndEdit メソッドを呼び出した時点の間です。 通常は、 RowChangingなどのイベントのハンドラー内のレコードの提案されたバージョンにアクセスします。 CancelEdit メソッドを呼び出すと、変更が元に戻され、提案されたバージョンのデータ行が削除されます。 |
元のバージョンと現在のバージョンは、更新情報がデータ ソースに送信されるときに便利です。 通常、更新プログラムがデータ ソースに送信されると、データベースの新しい情報はレコードの現在のバージョンに格納されます。 元のバージョンからの情報は、更新するレコードを検索するために使用されます。
たとえば、レコードの主キーが変更された場合、変更を更新するためにデータ ソース内の正しいレコードを見つける方法が必要です。 元のバージョンが存在しない場合、レコードはデータ ソースに追加される可能性が最も高く、余分な不要なレコードだけでなく、不正確で古い 1 つのレコードになります。 2 つのバージョンは、コンカレンシー制御でも使用されます。 元のバージョンをデータ ソース内のレコードと比較して、データセットに読み込まれてからレコードが変更されたかどうかを判断できます。
提案されたバージョンは、データセットへの変更を実際にコミットする前に検証を実行する必要がある場合に便利です。
レコードが変更された場合でも、その行の元のバージョンまたは現在のバージョンが常に存在するとは限りません。 テーブルに新しい行を挿入すると、元のバージョンはなく、現在のバージョンのみが存在します。 同様に、テーブルの Delete
メソッドを呼び出して行を削除すると、元のバージョンは存在しますが、現在のバージョンはありません。
データ行の HasVersion メソッドに対してクエリを実行することで、レコードの特定のバージョンが存在するかどうかをテストできます。 列の値を要求するときに、省略可能な引数として DataRowVersion 列挙値を渡すことで、いずれかのバージョンのレコードにアクセスできます。
変更されたレコードを取得する
データセット内のすべてのレコードを更新しないのが一般的な方法です。 たとえば、ユーザーは、多数のレコードを表示する Windows フォーム DataGridView コントロールを使用している可能性があります。 ただし、ユーザーは少数のレコードのみを更新し、1 つを削除して、新しいレコードを挿入することができます。 データセットとデータ テーブルは、変更された行のみを返すメソッド (GetChanges
) を提供します。
変更されたレコードのサブセットは、データ テーブル (GetChanges) またはデータセット (GetChanges) 自体のGetChanges
メソッドを使用して作成できます。 データ テーブルのメソッドを呼び出すと、変更されたレコードのみを含むテーブルのコピーが返されます。 同様に、データセットでメソッドを呼び出すと、変更されたレコードのみが含まれる新しいデータセットが取得されます。
GetChanges
それ自体は、変更されたすべてのレコードを返します。 これに対し、必要な DataRowState をパラメーターとして GetChanges
メソッドに渡すことで、新しく追加されたレコード、削除対象としてマークされるレコード、デタッチされたレコード、または変更されたレコードなど、変更されたレコードのサブセットを指定できます。
変更されたレコードのサブセットを取得することは、レコードを別のコンポーネントに送信して処理する場合に便利です。 データセット全体を送信する代わりに、コンポーネントに必要なレコードのみを取得することで、他のコンポーネントと通信するオーバーヘッドを削減できます。
データセットの変更をコミットする
データセットで変更が行われると、変更された行の RowState プロパティが設定されます。 レコードの元のバージョンと現在のバージョンは、 RowVersion プロパティによって確立、管理、および使用できるようになります。 これらの変更された行のプロパティに格納されているメタデータは、データ ソースに正しい更新を送信するために必要です。
変更にデータ ソースの現在の状態が反映されている場合は、この情報を保持する必要はなくなります。 通常、データセットとそのソースが同期されている場合は 2 回あります。
ソースからデータを読み取るときなど、データセットに情報を読み込んだ直後。
データセットからデータソースに変更を送信した後に(データベースに変更を送信するために必要な変更情報を失わないよう、以前には送信しないでください)。
AcceptChanges メソッドを呼び出すことで、保留中の変更をデータセットにコミットできます。 通常、 AcceptChanges は次の時刻に呼び出されます。
データセットを読み込んだ後。 TableAdapter の
Fill
メソッドを呼び出してデータセットを読み込む場合、アダプターは自動的に変更をコミットします。 ただし、別のデータセットをマージしてデータセットを読み込む場合は、変更を手動でコミットする必要があります。データセットの変更を XML Web サービスなどの別のプロセスに送信した後。
注意事項
この方法で変更をコミットすると、変更情報が消去されます。 データセットで行われた変更をアプリケーションで認識する必要がある操作の実行が完了するまで、変更をコミットしないでください。
この方法では、次の処理が実行されます。
AcceptChangesメソッドは、3 つのレベルで使用できます。 DataRow オブジェクトで呼び出して、その行の変更のみをコミットできます。 DataTable オブジェクトで呼び出して、テーブル内のすべての行をコミットすることもできます。 最後に、 DataSet オブジェクトで呼び出して、データセットのすべてのテーブルのすべてのレコードのすべての保留中の変更をコミットできます。
次の表では、メソッドが呼び出されるオブジェクトに基づいてコミットされる変更について説明します。
メソッド | 結果 |
---|---|
System.Data.DataRow.AcceptChanges | 変更は特定の行でのみコミットされます。 |
System.Data.DataTable.AcceptChanges | 変更は、特定のテーブルのすべての行でコミットされます。 |
System.Data.DataSet.AcceptChanges | 変更は、データセットのすべてのテーブルのすべての行に対してコミットされます。 |
注
TableAdapter の Fill
メソッドを呼び出してデータセットを読み込む場合、変更を明示的に受け入れる必要はありません。 既定では、 Fill
メソッドは、データ テーブルの設定が完了した後、 AcceptChanges
メソッドを呼び出します。
関連するメソッド RejectChanges、 Original バージョンを Current バージョンのレコードにコピーすることで、変更の影響を元に戻します。 また、各レコードの RowState を Unchangedに戻します。
データの検証
アプリケーション内のデータが渡されるプロセスの要件を満たしていることを確認するには、多くの場合、検証を追加する必要があります。 これには、フォーム内のユーザーのエントリが正しいことを確認したり、別のアプリケーションによってアプリケーションに送信されたデータを検証したり、コンポーネント内で計算された情報がデータ ソースとアプリケーションの要件の制約に含まれているかどうかを確認したりする場合があります。
データは、いくつかの方法で検証できます。
ビジネス 層で、データを検証するコードをアプリケーションに追加します。 データセットは、これを行うことができる 1 つの場所です。 データセットには、列と行の値が変化するにつれて変更を検証する機能など、バックエンド検証の利点がいくつかあります。 詳細については、「 データセット内のデータを検証する」を参照してください。
プレゼンテーション レイヤーで、フォームに検証を追加します。 詳細については、「 Windows フォームでのユーザー入力の検証」を参照してください。
データ バックエンドでは、データ ソース (データベースなど) にデータを送信し、データの受け入れまたは拒否を許可します。 データを検証し、エラー情報を提供するための高度な機能を備えたデータベースを使用している場合は、データの取得元に関係なくデータを検証できるため、実用的なアプローチになります。 ただし、この方法では、アプリケーション固有の検証要件に対応できない場合があります。 さらに、データ ソースでデータを検証すると、アプリケーションがバックエンドによって発生する検証エラーの解決を容易にする方法に応じて、データ ソースへのラウンド トリップが多数発生する可能性があります。
Von Bedeutung
Textに設定されているCommandType プロパティでデータ コマンドを使用する場合は、クライアントから送信された情報をデータベースに渡す前に注意深く確認してください。 悪意のあるユーザーは、未承認のアクセスを取得したり、データベースに損害を与えたりするために、変更または追加の SQL ステートメントを送信 (挿入) しようとする可能性があります。 ユーザー入力をデータベースに転送する前に、常に情報が有効であることを確認してください。 可能であれば、常にパラメーター化されたクエリまたはストアド プロシージャを使用することをお勧めします。
データ ソースに更新を送信する
データセットで変更が行われた後、変更をデータ ソースに送信できます。 最も一般的には、TableAdapter (またはデータ アダプター) の Update
メソッドを呼び出すことによってこれを行います。 このメソッドは、データ テーブル内の各レコードをループ処理し、必要な更新の種類 (更新、挿入、または削除) を決定してから、適切なコマンドを実行します。
更新方法の図として、アプリケーションで 1 つのデータ テーブルを含むデータセットを使用しているとします。 アプリケーションは、データベースから 2 つの行を取得します。 取得後、メモリ内データ テーブルは次のようになります。
(RowState) CustomerID Name Status
(Unchanged) c200 Robert Lyon Good
(Unchanged) c400 Nancy Buchanan Pending
アプリケーションによって、Nancy Buchanan の状態が "Preferred" に変更されます。この変更の結果、その行の RowState プロパティの値が Unchanged から Modifiedに変わります。 最初の行の RowState プロパティの値は Unchangedのままです。 データ テーブルは次のようになります。
(RowState) CustomerID Name Status
(Unchanged) c200 Robert Lyon Good
(Modified) c400 Nancy Buchanan Preferred
これで、アプリケーションで Update
メソッドが呼び出され、データセットがデータベースに送信されます。 メソッドは各行を順番に検査します。 最初の行の場合、このメソッドは SQL ステートメントをデータベースに送信しません。これは、その行が最初にデータベースからフェッチされてから変更されていないためです。
ただし、2 行目の場合、 Update
メソッドは自動的に正しいデータ コマンドを呼び出し、データベースに送信します。 SQL ステートメントの具体的な構文は、基になるデータ ストアでサポートされている SQL の方言によって異なります。 ただし、送信される SQL ステートメントの次の一般的な特徴は注目に値します。
送信される SQL ステートメントは、
UPDATE
ステートメントです。 アダプターは、RowState プロパティの値がModifiedされているため、UPDATE
ステートメントを使用することを認識します。転送される SQL ステートメントには、
UPDATE
ステートメントのターゲットがCustomerID = 'c400'
行であることを示すWHERE
句が含まれています。CustomerID
はターゲット テーブルの主キーであるため、SELECT
ステートメントのこの部分はターゲット行を他のすべての行と区別します。WHERE
句の情報は、行の識別に必要な値が変更された場合に、レコードの元のバージョン (DataRowVersion.Original
) から派生します。送信される SQL ステートメントには、変更された列の新しい値を設定する
SET
句が含まれています。注
TableAdapter の
UpdateCommand
プロパティがストアド プロシージャの名前に設定されている場合、アダプターは SQL ステートメントを構築しません。 代わりに、渡された適切なパラメーターを使用してストアド プロシージャを呼び出します。
パラメーターを渡す
通常、パラメーターを使用して、データベースで更新されるレコードの値を渡します。 TableAdapter の Update
メソッドで UPDATE
ステートメントを実行する場合は、パラメーター値を入力する必要があります。 適切なデータ コマンド (この場合は TableAdapter のUpdateCommand
オブジェクト) のParameters
コレクションからこれらの値を取得します。
Visual Studio ツールを使用してデータ アダプターを生成した場合、 UpdateCommand
オブジェクトには、ステートメント内の各パラメーター プレースホルダーに対応するパラメーターのコレクションが含まれます。
各パラメーターの System.Data.SqlClient.SqlParameter.SourceColumn プロパティは、データ テーブル内の列を指します。 たとえば、au_id
パラメーターと Original_au_id
パラメーターのSourceColumn
プロパティは、データ テーブル内の任意の列に作成者 ID が含まれている値に設定されます。アダプターの Update
メソッドを実行すると、更新中のレコードから作成者 ID 列が読み取られ、値がステートメントに入力されます。
UPDATE
ステートメントでは、新しい値 (レコードに書き込まれる値) と古い値の両方を指定する必要があります (レコードをデータベースに配置できるようにします)。 そのため、値ごとに 2 つのパラメーターがあります。1 つは SET
句用、別のパラメーターは WHERE
句用です。 どちらのパラメーターも更新中のレコードからデータを読み取りますが、パラメーターの SourceVersion プロパティに基づいて異なるバージョンの列値を取得します。 SET
句のパラメーターは現在のバージョンを取得し、WHERE
句のパラメーターは元のバージョンを取得します。
注
Parameters
コレクションの値をコードで自分で設定することもできます。これは通常、データ アダプターのRowChanging イベントのイベント ハンドラーで行います。
関連コンテンツ
- Visual Studio のデータセットツール
- TableAdapters の作成と構成
- TableAdapter を使用してデータを更新する
- Visual Studio でコントロールをデータにバインドする
- データの検証
- 方法: エンティティを追加、変更、削除する (WCF データ サービス)