DataAdapter によるデータ ソースの更新 (ADO.NET)

DataAdapter の Update メソッドを呼び出して、変更を DataSet からデータ ソースに反映します。 Update メソッドは、Fill メソッドと同様に、引数として DataSet のインスタンスおよびオプションの DataTable オブジェクトまたは DataTable 名を受け取ります。 DataSet のインスタンスは、行われた変更点を格納する DataSet です。DataTable は、変更点の取得元のテーブルです。 DataTable を指定しなかった場合、DataSet 内の最初の DataTable が使用されます。

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

メモメモ

SQL Server のストアド プロシージャで、DataAdapter を使用してデータを編集または削除する場合、ストアド プロシージャの定義に SET NOCOUNT ON は使用しないでください。処理された行数がゼロとして返され、DataAdapter によって同時実行の競合として解釈されてしまいます。この場合、DBConcurrencyException がスローされます。

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

メモメモ

DataTable の行を Delete することと、行を Remove することの違いを理解することが大切です。Remove メソッドまたは RemoveAt メソッドを呼び出した場合、行は直ちに削除されます。その後、DataTable または DataSet を DataAdapter に渡し、Update を呼び出しても、バックエンド データ ソースの対応する行には影響しません。Delete メソッドを使用した場合、行はそのまま DataTable 内に維持され、削除対象としてマークされます。その後、DataTable または DataSet を DataAdapter に渡し、Update を呼び出すと、バックエンド データ ソースから対応する行が削除されます。

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

UpdatedRowSource を使用した値の DataSet への割り当て

DbCommand オブジェクトの UpdatedRowSource プロパティを使用すると、DataAdapter の Update メソッドを呼び出した後、データ ソースから返された値を DataTable に割り当てる方法を制御できます。 UpdatedRowSource プロパティを UpdateRowSource 列挙型の値の 1 つに設定することで、DataAdapter コマンドが返した出力パラメーターを無視するか、DataSet 内の変更行に適用するかを制御できます。 最初に返された行 (存在する場合) を、DataTable 内の変更行に適用するかどうかを指定することもできます。

UpdateRowSource 列挙型のさまざまの値と、それらの値が DataAdapter で使用されるコマンドの動作にどのように影響するかを次の表で説明します。

UpdatedRowSource 列挙型

説明

Both

出力パラメーターと返された結果セットの最初の行を DataSet 内の変更行に割り当てます。

FirstReturnedRecord

返された結果セットの最初の行のデータだけを DataSet 内の変更行に割り当てます。

None

出力パラメーターまたは返された結果セットの行が無視されます。

OutputParameters

出力パラメーターだけを DataSet 内の変更行に割り当てます。

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

メモメモ

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

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

DataSet、DataTable、または DataRow に対して AcceptChanges を呼び出すと、DataRow のすべての Original 値が DataRow の Current 値で上書きされます。 行を一意に識別するフィールド値が変更された場合は、AcceptChanges 呼び出しの後に Original 値がデータ ソースの値と一致しなくなります。 AcceptChanges は、DataAdapter の Update メソッドを呼び出す間に、各行について自動的に呼び出されます。 Update メソッドの呼び出し中に元の値を維持するには、まず DataAdapter の AcceptChangesDuringUpdate プロパティを false に設定するか、RowUpdated イベントのイベント ハンドラーを作成し、その StatusSkipCurrentRow に設定します。 詳細については、「DataSet の内容のマージ (ADO.NET)」および「DataAdapter のイベント処理 (ADO.NET)」を参照してください。

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

Private Sub AdapterUpdate(ByVal connectionString As String)

    Using connection As SqlConnection = New SqlConnection( _
       connectionString)

        Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
          "SELECT CategoryID, CategoryName FROM dbo.Categories", _
          connection)

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

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

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

        Dim categoryTable As New DataTable
        adapter.Fill(categoryTable)

        Dim categoryRow As DataRow = categoryTable.Rows(0)
        categoryRow("CategoryName") = "New Beverages"

        adapter.Update(categoryTable)

        Console.WriteLine("Rows after update.")
        Dim row As DataRow
        For Each row In categoryTable.Rows
            Console.WriteLine("{0}: {1}", row(0), row(1))
        Next
    End Using
End Sub
private static void AdapterUpdate(string connectionString)
{
    using (SqlConnection connection =
               new SqlConnection(connectionString))
    {
        SqlDataAdapter dataAdpater = new SqlDataAdapter(
          "SELECT CategoryID, CategoryName FROM Categories",
          connection);

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

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

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

        DataTable categoryTable = new DataTable();
        dataAdpater.Fill(categoryTable);

        DataRow categoryRow = categoryTable.Rows[0];
        categoryRow["CategoryName"] = "New Beverages";

        dataAdpater.Update(categoryTable);

        Console.WriteLine("Rows after update.");
        foreach (DataRow row in categoryTable.Rows)
        {
            {
                Console.WriteLine("{0}: {1}", row[0], row[1]);
            }
        }
    }
}

AutoIncrement 列

データ ソースから取得したテーブルに自動インクリメント列がある場合、自動インクリメント値をストアド プロシージャの出力パラメーターとして取得してそれをテーブルの列に割り当てるか、ストアド プロシージャまたは SQL ステートメントによって返された結果セットの最初の行の自動インクリメント値を取得するか、または DataAdapter の RowUpdated イベントを使用して追加の SELECT コマンドを実行することによって、DataSet の列に値を格納できます。 詳細と例については、「ID 値および Autonumber 値の取得 (ADO.NET)」を参照してください。

挿入、更新、削除の順序

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

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

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

Dim table As DataTable = dataSet.Tables("Customers")

' First process deletes.
dataSet.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.Deleted))

' Next process updates.
adapter.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.ModifiedCurrent))

' Finally, process inserts.
dataAdpater.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.Added))
DataTable table = dataSet.Tables["Customers"];

// First process deletes.
adapter.Update(table.Select(null, null, DataViewRowState.Deleted));

// Next process updates.
adapter.Update(table.Select(null, null, 
  DataViewRowState.ModifiedCurrent));

// Finally, process inserts.
adapter.Update(table.Select(null, null, DataViewRowState.Added));

参照

概念

行の状態とバージョン

AcceptChanges と RejectChanges

DataSet の内容のマージ (ADO.NET)

ID 値および Autonumber 値の取得 (ADO.NET)

その他の技術情報

DataAdapter と DataReader (ADO.NET)