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

Merge メソッドを使用して、DataSetDataTable、または DataRow の配列の内容を既存の DataSet にマージできます。 いくつかの要因とオプションが、新しいデータを既存の DataSet にマージする方法に影響します。

主キー

マージによって新しいデータとスキーマを受け取るテーブルに主キーがある場合、受信データの新しい行と、Original 行バージョンの主キーの値が受信データの主キーの値と同じである既存の行が照合されます。 受信スキーマの列が既存のスキーマの列と一致する場合、既存の行にあるデータが変更されます。 既存のスキーマと一致しない列は、MissingSchemaAction パラメーターに基づいて無視または追加されます。 主キーの値が既存の行と一致しない新しい行は、既存のテーブルに追加されます。

受信する行または既存の行の状態が Added の場合、Original 行バージョンが存在しないため、Added の行の Current 行バージョンの主キーの値を使用して、その 2 つの行の主キーの値を照合します。

受信テーブルと既存のテーブルに、名前が同じでデータ型が異なる列が含まれている場合、例外がスローされ、DataSet の MergeFailed イベントが発生します。 受信テーブルと既存のテーブルの両方にキーが定義されていても、主キーの対象の列が異なる場合、例外がスローされ、DataSet の MergeFailed イベントが発生します。

マージによって新しいデータを受け取るテーブルに主キーがない場合、受信データの新しい行とそのテーブルの既存の行は一致しません。その代わりに新しい行が既存のテーブルに追加されます。

テーブル名と名前空間

DataTable オブジェクトには、Namespace プロパティ値を割り当てることができます。 Namespace の値が割り当てられている場合、DataSet には、TableName の値が同じである複数の DataTable オブジェクトを含めることができます。 マージ操作の際には、TableNameNamespace の両方を使用してマージの対象を識別します。 Namespace が割り当てられていない場合、TableName のみを使用してマージの対象を識別します。

メモメモ

この動作は .NET Framework version 2.0 で変更されました。.NET Framework version 1.1 では、名前空間はサポートされていましたが、マージ操作中には無視されました。そのため、Namespace プロパティ値を使用する DataSet の動作は、実行されている .NET Framework のバージョンに応じて変わります。たとえば、DataTables を含む 2 つの DataSets があり、どちらの TableName プロパティの値も同じですが、Namespace プロパティの値が異なっているとします。.NET Framework version 1.1 では、この 2 つの DataSet オブジェクトをマージするときには Namespace の名前の違いが無視されます。しかし、バージョン 2.0 以降では、マージを実行すると対象の DataSet に新しい DataTables が 2 つ作成されます。元の DataTables はマージの影響を受けません。

PreserveChanges

DataSet、DataTable、または DataRow の各配列を Merge メソッドに渡すときに、オプション パラメーターを含めることができます。そのパラメーターを使用して、変更内容を既存の DataSet に保存するかどうか、および受信データで見つかった新しいスキーマの要素を処理する方法を指定します。 受信データの後に続く最初のオプション パラメーターは、Boolean 型のフラグ PreserveChanges で、変更内容を既存の DataSet に保存するかどうかを指定します。 PreserveChanges フラグを true に設定した場合、既存の行の Current 行バージョンの値は受信する値で上書きされません。 PreserveChanges フラグを false に設定した場合、既存の行の Current 行バージョンの値が受信した値で上書きされます。 PreserveChanges フラグを指定しない場合は、既定で false に設定されます。 行バージョンの詳細については、「行の状態とバージョン」を参照してください。

PreserveChanges を true に設定すると、既存の行のデータは Current 行バージョンで保存されますが、既存の行の Original 行バージョンのデータは受信した行の Original 行バージョンのデータで上書きされます。 既存の行の RowState は、Modified に設定されます。 適用される例外を次に示します。

  • 既存の行の RowState が Deleted の場合、この RowState は Deleted のままであり、Modified には設定されません。 この場合、受信した行のデータは既存の行の Original 行バージョンとして保存され、既存の行の Original 行バージョンのデータが上書きされます (受信する行の RowState が Added でない場合)。

  • 受信した行の RowState が Added の場合、受信した行には Original 行バージョンが存在しないため、既存の行の Original 行バージョンのデータは受信した行のデータで上書きされません。

PreserveChanges が false の場合、既存の行の Current と Original の両方の行バージョンが受信した行のデータで上書きされます。さらに、既存の行の RowState が、受信した行の RowState に設定されます。 適用される例外を次に示します。

  • 受信した行の RowState が Unchanged であり、既存の行の RowState が Modified、Deleted、または Added である場合、既存の行の RowState は Modified に設定されます。

  • 受信した行の RowState が Added であり、既存の行の RowState が Unchanged、Modified、または Deleted である場合、既存の行の RowState は Modified に設定されます。 また、受信した行には Original 行バージョンが存在しないため、既存の行の Original 行バージョンのデータは、受信した行のデータで上書きされません。

MissingSchemaAction

Merge メソッドのオプションである MissingSchemaAction パラメーターを使用して、既存の DataSet に含まれない受信データのスキーマ要素を Merge で処理する方法を指定できます。

次の表で、MissingSchemaAction のオプションについて説明します。

MissingSchemaAction のオプション

説明

Add

新しいスキーマ情報を DataSet に追加し、受信した値を新しい列に読み込みます。 これは、既定の設定です。

AddWithKey

新しいスキーマおよび主キーの情報を DataSet に追加し、受信した値を新しい列に読み込みます。

Error

一致しないスキーマ情報が見つかった場合、例外をスローします。

Ignore

新しいスキーマ情報を無視します。

制約

Merge メソッドでは、新しいデータがすべて既存の DataSet に追加されるまで制約がチェックされません。 データが追加されると、DataSet の現在の値に制約が適用されます。 開発者は、制約違反のためにスローされる例外をコードで処理する必要があります。

DataSet 内に、Unchanged に設定され、主キー値が 1 である既存の行があるとします。 マージ操作の際、受信した行が Modified に設定され、Original 行バージョンの主キーの値が 2 であり、Current 行バージョンの主キーの値が 1 である場合、Original 行バージョンの主キーの値が一致しないため、既存の行と受信した行は一致していると見なされません。 マージが完了し、制約がチェックされると、Current 行バージョンの主キーの値が主キー列の UNIQUE 制約に違反するため、例外がスローされます。

メモメモ

ID 列などの自動インクリメント列を含むデータベース テーブルに行を挿入すると、挿入によって返される ID 列の値が DataSet の列の値と一致せず、返された列がマージされずに追加されることがあります。詳細については、「ID 値および Autonumber 値の取得 (ADO.NET)」を参照してください。

次のコード サンプルでは、スキーマが異なる 2 つの DataSet オブジェクトをマージし、その 2 つの受信 DataSet オブジェクトのスキーマを組み合わせたスキーマを持つ 1 つの DataSet を作成します。

Using connection As SqlConnection = New SqlConnection( _
   connectionString)

    Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
      "SELECT CustomerID, CompanyName FROM Customers", connection)

    connection.Open()

    Dim customers As DataSet = New DataSet()
    adapter.FillSchema(customers, SchemaType.Source, "Customers")
    adapter.Fill(customers, "Customers")

    Dim orders As DataSet = New DataSet()
    orders.ReadXml("Orders.xml", XmlReadMode.ReadSchema)
    orders.AcceptChanges()

    customers.Merge(orders, True, MissingSchemaAction.AddWithKey)
End Using
using (SqlConnection connection =
           new SqlConnection(connectionString))
{
    SqlDataAdapter adapter = 
        new SqlDataAdapter(
        "SELECT CustomerID, CompanyName FROM dbo.Customers", 
        connection);

    connection.Open();

    DataSet customers = new DataSet();
    adapter.FillSchema(customers, SchemaType.Source, "Customers");
    adapter.Fill(customers, "Customers");

    DataSet orders = new DataSet();
    orders.ReadXml("Orders.xml", XmlReadMode.ReadSchema);
    orders.AcceptChanges();

    customers.Merge(orders, true, MissingSchemaAction.AddWithKey);

次のコード サンプルでは、更新内容を含む既存の DataSet を取得し、その更新内容を DataAdapter に渡してデータ ソースで処理します。 次に、その結果を元の DataSet にマージします。 エラーとなった変更内容が拒否された後、マージされた変更内容が AcceptChanges を使用してコミットされます。

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

            ' Make modifications to the Customers table.

            ' Get changes to the DataSet.
            Dim dataSetChanges As DataSet = dataSet.GetChanges()

            ' Add an event handler to handle the errors during Update.
            AddHandler adapter.RowUpdated, New SqlRowUpdatedEventHandler( _
              AddressOf OnRowUpdated)

            connection.Open()
            adapter.Update(dataSetChanges, "Customers")
            connection.Close()

            ' Merge the updates.
            dataSet.Merge(dataSetChanges, True, MissingSchemaAction.Add)

            ' Reject changes on rows with errors and clear the error.
            Dim errRows() As DataRow = dataSet.Tables("Customers").GetErrors()
            Dim errRow As DataRow
            For Each errRow In errRows
                errRow.RejectChanges()
                errRow.RowError = Nothing
            Next

            ' Commit the changes.
            dataSet.AcceptChanges()

            DataTable customers = dataSet.Tables["Customers"];

            // Make modifications to the Customers table.

            // Get changes to the DataSet.
            DataSet dataSetChanges = dataSet.GetChanges();

            // Add an event handler to handle the errors during Update.
            adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);

            connection.Open();
            adapter.Update(dataSetChanges, "Customers");
            connection.Close();

            // Merge the updates.
            dataSet.Merge(dataSetChanges, true, MissingSchemaAction.Add);

            // Reject changes on rows with errors and clear the error.
            DataRow[] errRows = dataSet.Tables["Customers"].GetErrors();
            foreach (DataRow errRow in errRows)
            {
                errRow.RejectChanges();
                errRow.RowError = null;
            }

            // Commit the changes.
            dataSet.AcceptChanges();

Private Sub OnRowUpdated( _
    ByVal sender As Object, ByVal args As SqlRowUpdatedEventArgs)
    If args.Status = UpdateStatus.ErrorsOccurred Then
        args.Row.RowError = args.Errors.Message
        args.Status = UpdateStatus.SkipCurrentRow
    End If
End Sub
protected static void OnRowUpdated(
    object sender, SqlRowUpdatedEventArgs args)
{
    if (args.Status == UpdateStatus.ErrorsOccurred)
    {
        args.Row.RowError = args.Errors.Message;
        args.Status = UpdateStatus.SkipCurrentRow;
    }
}

参照

概念

行の状態とバージョン

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

その他の技術情報

DataSets、DataTables、および DataViews (ADO.NET)

DataAdapter と DataReader (ADO.NET)

ADO.NET でのデータの取得および変更