DataAdapter および DataSet によるデータベースの更新

DataAdapterUpdate メソッドを呼び出して、変更を DataSet からデータ ソースに反映します。Update メソッドは、Fill メソッドと同じように、引数として DataSet のインスタンスおよびオプションの DataTable オブジェクトまたは DataTable 名を受け取ります。DataSet のインスタンスは、行われた変更を格納する DataSet です。DataTable は、変更の取得元のテーブルです。

Update メソッドを呼び出すと、DataAdapter は、既に加えられた変更を解析し、適切なコマンド (INSERT、UPDATE、または DELETE) を実行します。DataAdapterDataRow へ加えられた変更を検出すると、InsertCommandUpdateCommand、または DeleteCommand を使用してその変更を処理します。その結果、デザイン時にコマンド構文を指定し、可能な場合はストアド プロシージャを使用することにより、ADO.NET アプリケーションのパフォーマンスを最適化できます。コマンドは Update を呼び出す前に明示的に設定する必要があります。Update を呼び出し、その更新に関連する適切なコマンドが存在しない場合 (たとえば、削除済み行に関連する DeleteCommand が存在しない場合) は、例外がスローされます。

Command パラメータを使用して、DataSet 内の各変更行に対する SQL ステートメントまたはストアド プロシージャに入力値と出力値を指定できます。詳細については、「DataAdapter によるパラメータの使用」を参照してください。

DataTable を単一データベース テーブルに割り当てたり、単一データベースから生成する場合は、CommandBuilder オブジェクトを利用して自動的に DataAdapterDeleteCommandInsertCommand、および UpdateCommand を生成できます。詳細については、「自動生成コマンド」を参照してください。

Update メソッドは変更を元のデータ ソースに反映させますが、DataSet に最後にデータを格納した後、他のクライアントがデータ ソースのデータを変更した可能性もあります。DataSet を現在のデータで更新するには、DataAdapter を使用し、DataSet を再び Fill します。新しい行がテーブルに追加され、更新された情報が既存の行に取り込まれます。Fill メソッドは、DataSet の行と SelectCommand によって返された行の主キーの値を調べて、新しい行が追加されたか、または既存の行が更新されたかを判断します。Fill メソッドは、SelectCommand によって返された結果の行と同じ主キーの値を持つ DataSet の行を見つけた場合、SelectCommand によって返された行の情報で既存の行を更新して、既存の行の RowStateUnchanged に設定します。SelectCommand によって返された行の主キーの値が、DataSet のどの行の主キーの値にも一致しない場合、Fill メソッドは、RowStateUnchanged の新しい行を追加します。

メモ   SelectCommand が OUTER JOIN の結果を返す場合、DataAdapter は、生成される DataTablePrimaryKey 値を設定しません。自分で PrimaryKey を定義して、重複行が正しく反映されるようにする必要があります。詳細については、「テーブルの主キーの定義」を参照してください。

Update 時に発生する例外を処理するには、行更新エラーが発生したときに RowUpdated イベントを使用して応答するか (「DataAdapter イベントの使用」を参照)、または Update 呼び出しの前に DataAdapter.ContinueUpdateOnErrortrue に設定し、Update が完了した時点で特定の行の RowError プロパティに格納されているエラー情報に応答します (「行のエラー情報の追加と読み取り」を参照してください)。

メモ   DataSetDataTable、または DataRow に対して AcceptChanges を呼び出すと、DataRow のすべての Original 値が DataRowCurrent 値で上書きされます。行を一意に識別するフィールド値が変更された場合は、AcceptChanges 呼び出しの後、Original 値はデータ ソースの値と一致しなくなります。

DataAdapterUpdateCommand を明示的に設定して、変更済みの行に対して更新を実行する方法を次の例に示します。UPDATE ステートメントの WHERE 句に指定したパラメータが SourceColumnOriginal 値を使用するように設定されていることに注意してください。Current 値が既に変更されている可能性、そしてデータ ソースの値と一致していない可能性があるため、この設定は重要です。Original 値は、データ ソースから DataTable にデータを取得するために使用された値です。

SqlClient

Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _
                                     "WHERE CategoryID = @CategoryID", nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")  

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);       

catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +
                                     "WHERE CategoryID = @CategoryID" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");   

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";

catDA.Update(catDS);

OleDb

Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)       

catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _
                                       "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")

Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);            

catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? " +
                                       "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");

OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);

Odbc

Dim catDA As OdbcDataAdapter = New OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New OdbcCommand("UPDATE Categories SET CategoryName = ? " & _
                                      "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName")

Dim workParm As OdbcParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)

cRow("CategoryName") = "New Category"

Dim modRows() As DataRow = catDS.Tables("Categories").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent)

catDA.Update(modRows)
[C#]
OdbcDataAdapter catDA = new OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

catDA.UpdateCommand = new OdbcCommand("UPDATE Categories SET CategoryName = ? " +
                                      "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName");

OdbcParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];

cRow["CategoryName"] = "New Category";

DataRow[] modRows = catDS.Tables["Categories"].Select(null, null, DataViewRowState.ModifiedCurrent);
catDA.Update(modRows);

AutoIncrement 列

データ ソースからのテーブルに自動インクリメント列がある場合、データ ソースによって生成された値を DataSet の列に格納するには、自動インクリメント値をストアド プロシージャの出力パラメータとして返しそれをテーブルの列に割り当てるか、または DataAdapterRowUpdated イベントを使用します。この例については、「ID 値および Autonumber 値の取得」を参照してください。

ただし、DataSet 内の値はデータ ソースの値と同期しなくなるため、予期しない動作を発生する場合があります。たとえば、CustomerID という自動インクリメント主キー列を持つテーブルがあるとします。DataSet 内に新しい 2 人の顧客を追加した場合、これらの顧客は自動インクリメント CustomerId 値 1 および 2 を受け取ります。2 行目の顧客行が DataAdapterUpdate メソッドに渡されると、新しく追加された行は、データ ソースで自動インクリメント CustomerID 値 1 を受け取ります。これは DataSet 内の値 2 とは一致しません。DataAdapterDataSet の行に戻り値を設定すると、1 行目の顧客行が既に CustomerID 1 を持つため制約違反が発生します。

このような動作を防ぐには、データ ソースの自動インクリメント列および DataSet の自動インクリメント値を使用するときに、AutoIncrementStep を –1、AutoIncrementSeed を 0 と設定して DataSet に列を作成します。同時に、1 から始まり正のステップ値でインクリメントする自動インクリメント ID 値がデータ ソースでを生成されるようにします。その結果、DataSet では自動インクリメント値として負の数値が生成されるため、データ ソースで生成される正の自動インクリメント値と矛盾しなくなります。もう 1 つの方法は、自動インクリメント列の代わりに Guid 型の列を使用することです。Guid 値生成のアルゴリズムでは、データ ソースで生成される Guid と同じ GuidDataSet で生成されることはありません。DataTable の列を定義する方法の詳細については、「DataTable のスキーマの定義」を参照してください。

挿入、更新、削除の順序

通常の条件下では、DataSet を使用して行う変更の順序をデータ ソースに送信することが重要です。たとえば、既存の行の主キーの値を更新し、その新しい主キーの値で新しい行を追加する場合、更新は挿入の前に処理する必要があります。

DataTableSelect メソッドを使用すると、特定の RowState を持つ行だけを参照する DataRow 配列を返すことができます。その後で、返された DataRow 配列を DataAdapterUpdate メソッドに渡して変更行を処理できます。更新する行のサブセットを指定することで、挿入、更新、および削除の処理順序を制御できます。

たとえば次のコードでは、テーブルの削除行を最初に処理し、次に更新行、最後に挿入行を処理します。

Dim updTable As DataTable = custDS.Tables("Customers")

' First process deletes.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Deleted))

' Next process updates.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))

' Finally, process inserts.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Added))
[C#]
DataTable updTable = custDS.Tables["Customers"];

// First process deletes.
custDA.Update(updTable.Select(null, null, DataViewRowState.Deleted));

// Next process updates.
custDA.Update(updTable.Select(null, null, DataViewRowState.ModifiedCurrent));

// Finally, process inserts.
custDA.Update(updTable.Select(null, null, DataViewRowState.Added));

参照

.NET Framework データ プロバイダによるデータのアクセス | DataSet クラス | DataTable クラス | OleDbDataAdapter クラス | OdbcDataAdapter クラス | SqlDataAdapter クラス