合并数据集内容

可以使用Merge方法将DataSetDataTableDataRow数组的内容合并到现有DataSet中。 若干因素和选项会影响将新数据合并到现有 DataSet 中的方式。

主键

如果从合并接收新数据和架构的表具有主键,则传入数据中的新行将与现有行匹配,这些现有行具有与传入数据中的行相同的Original主键值。 如果传入架构中的列与现有架构的列匹配,则会修改现有行中的数据。 根据参数忽略或添加与现有架构不匹配的 MissingSchemaAction 列。 与任何现有行不匹配的主键值的新行将追加到现有表中。

如果传入行或现有行具有行状态 Added,则其主键值使用 Current 行的主 Added 键值进行匹配,因为不存在 Original 行版本。

如果传入表和现有表包含具有相同名称但数据类型不同的列,则会引发异常并触发 MergeFailedDataSet 事件。 如果传入表和现有表都具有已定义的键,但是主键针对不同的列,将引发异常,并引发 MergeFailedDataSet 事件。

如果从合并接收新数据的表没有主键,则传入数据中的新行无法与表中的现有行匹配,而是追加到现有表中。

表名称和命名空间

DataTable 对象可以选择被分配一个 Namespace 属性值。 如果分配了 Namespace 值,则一个 DataSet 可以包含多个具有相同 DataTable 值的 TableName 对象。 在合并操作期间,两者TableNameNamespace都用于标识合并的目标。 如果Namespace未分配,则仅使用TableName来标识合并的目标。

注释

.NET Framework 版本 2.0 中更改了此行为。 在版本 1.1 中,支持命名空间,但在合并作期间被忽略。 出于此原因, DataSet 使用 Namespace 属性值的行为将有所不同,具体取决于运行的 .NET Framework 版本。 例如,假设你有两个 DataSets ,两个 DataSets 都包含 DataTables ,并且它们的 TableName 属性值相同,但 Namespace 属性值不同。 在 .NET Framework 版本 1.1 中,合并这两DataSet个对象时,将忽略不同的Namespace名称。 但是,从版本 2.0 开始,合并会导致在目标DataSet中创建两个新DataTables项。 原始 DataTables 副本不受合并影响。

PreserveChanges

将 、DataSetDataTableDataRow数组传递给Merge方法时,可以包含可选参数,这些参数指定是否保留现有DataSet更改,以及如何处理传入数据中找到的新架构元素。 传入数据后的第一个参数是布尔标志, PreserveChanges它指定是否保留现有 DataSet更改。 当PreserveChanges标志设置为true时,传入值不会覆盖现有行版本中Current的现有值。 如果将PreserveChanges标志设置为false,传入的值将覆盖Current行版本中现有行的现有值。 PreserveChanges如果未指定标志,则默认将其设置为false。 有关行版本的详细信息,请参阅 行状态和行版本

如果 PreserveChangestrue,则现有行的 Current 行版本中将保持现有行中的数据,而现有行的 Original 行版本中的数据将由传入行的 Original 行版本中的数据覆盖。 现有的 RowState 行已设置为 Modified。 存在以下例外:

  • 如果现有行的RowStateDeleted,则这个RowState保持为Deleted,不会被设置为Modified。 在这种情况下,传入行中的数据将仍然存储在现有行的 Original 行版本中,并覆盖现有行的 Original 行版本(除非传入行的 RowStateAdded)。

  • 如果传入行的 RowStateAdded,则现有行的 Original 行版本中的数据将不会由传入行中的数据覆盖,因为传入行不具有 Original 行版本。

PreserveChangesfalse 时,现有行中的 CurrentOriginal 行版本都被传入行的数据覆盖,现有行的 RowState 被设置为传入行的 RowState。 存在以下例外:

  • 如果传入行的 RowStateUnchanged 且现有行的 RowStateModifiedDeletedAdded,则现有行的 RowState 将设置为 Modified

  • 如果传入的行有一个RowState值为Added,并且现有行有一个RowState值为UnchangedModifiedDeleted,则现有行的RowState设置为Modified。 此外,由于传入行没有 Original 行版本,因此不会用传入行中的数据覆盖现有行的 Original 行版本中的数据。

MissingSchemaAction

可以使用方法的Merge可选MissingSchemaAction参数来指定如何处理Merge传入数据中不属于现有DataSet数据的架构元素。

下表描述了 MissingSchemaAction 的选项。

MissingSchemaAction 选项 DESCRIPTION
Add 将新的架构信息添加到 DataSet 新列,并使用传入值填充新列。 这是默认值。
AddWithKey 将新的架构和主键信息添加到 DataSet 新列,并使用传入值填充新列。
Error 如果遇到不匹配的架构信息,则抛出异常。
Ignore 忽略新的架构信息。

约束

使用Merge方法时,只有在所有新数据添加到现有DataSet后,才会检查约束。 添加数据后,对当前 DataSet值强制实施约束。 必须确保代码处理因约束冲突而引发的任何异常。

设想有这样一种情况:DataSet 中的某一现有行是主键值为 1 的 Unchanged。 在与一行主键值为 2 的Modified传入行和一行主键值为 1 的Current传入行进行合并操作时,由于主键值不同,现有行和传入行不被视为匹配。 但是,完成合并并检查约束时,将引发异常,因为 Current 主键值违反了主键列的唯一约束。

注释

当行插入到包含自动递增列(如标识列)的数据库表中时,插入返回的标识列值可能与其中 DataSet的值不匹配,导致返回的行追加而不是合并。 有关详细信息,请参阅 检索标识或自动编号值

下面的代码示例将两 DataSet 个具有不同架构的对象合并为一个 DataSet 对象,以及两个传入 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

另请参阅