DataSet の内容のマージ
Merge メソッドを使用して、DataSet、DataTable、または DataRow の配列の内容を既存の DataSet
にマージできます。 いくつかの要因とオプションが、新しいデータを既存の DataSet
にマージする方法に影響します。
主キー
マージによって新しいデータとスキーマを受け取るテーブルに主キーがある場合、受信データの新しい行と、Original 行バージョンの主キーの値が受信データの主キーの値と同じである既存の行が照合されます。 受信スキーマの列が既存のスキーマの列と一致する場合、既存の行にあるデータが変更されます。 既存のスキーマと一致しない列は、MissingSchemaAction パラメーターに基づいて無視または追加されます。 主キーの値が既存の行と一致しない新しい行は、既存のテーブルに追加されます。
受信する行または既存の行の状態が Added の場合、Current 行バージョンが存在しないため、Added
の行の Original
行バージョンの主キーの値を使用して、その 2 つの行の主キーの値を照合します。
受信テーブルと既存のテーブルに、名前が同じでデータ型が異なる列が含まれている場合、例外がスローされ、MergeFailed の DataSet
イベントが発生します。 受信テーブルと既存のテーブルの両方にキーが定義されていても、主キーの対象の列が異なる場合、例外がスローされ、MergeFailed
の DataSet
イベントが発生します。
マージによって新しいデータを受け取るテーブルに主キーがない場合、受信データの新しい行とそのテーブルの既存の行は一致しません。その代わりに新しい行が既存のテーブルに追加されます。
テーブル名と名前空間
DataTable オブジェクトには、Namespace プロパティ値を割り当てることができます。 Namespace の値が割り当てられている場合、DataSet には、DataTable の値が同じである複数の TableName オブジェクトを含めることができます。 マージ操作の際には、TableName と Namespace の両方を使用してマージの対象を識別します。 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
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
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