DataSet の内容のマージ

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

主キー

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

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

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

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

テーブル名と名前空間

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

Note

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

PreserveChanges

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

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

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

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

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

  • 受信した行の RowStateUnchanged であり、既存の行の RowStateModifiedDeleted、または Added である場合、既存の行の RowStateModified に設定されます。

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

MissingSchemaAction

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

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

MissingSchemaAction のオプション 説明
Add 新しいスキーマ情報を DataSet に追加し、受信した値を新しい列に読み込みます。 既定値です。
AddWithKey 新しいスキーマおよび主キーの情報を DataSet に追加し、受信した値を新しい列に読み込みます。
Error 一致しないスキーマ情報が見つかった場合、例外をスローします。
Ignore 新しいスキーマ情報を無視します。

制約

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

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

Note

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

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

using (SqlConnection connection =
           new(connectionString))
{
    SqlDataAdapter adapter =
        new(
        "SELECT CustomerID, CompanyName FROM dbo.Customers",
        connection);

    connection.Open();

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

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

    customers.Merge(orders, true, MissingSchemaAction.AddWithKey);
Using connection As SqlConnection = New SqlConnection( _
   connectionString)

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

    connection.Open()

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

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

    customers.Merge(orders, True, MissingSchemaAction.AddWithKey)
End Using

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

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

// Make modifications to the Customers table.

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

// Add an event handler to handle the errors during Update.
adapter.RowUpdated += 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();

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()

protected static void OnRowUpdated(
    object sender, SqlRowUpdatedEventArgs args)
{
    if (args.Status == UpdateStatus.ErrorsOccurred)
    {
        args.Row.RowError = args.Errors!.Message;
        args.Status = UpdateStatus.SkipCurrentRow;
    }
}
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

関連項目