Поделиться через


Объединение содержимого набора данных

Вы можете использовать метод Merge для объединения содержимого массива DataSet, DataTable или DataRow в существующий DataSet. Несколько факторов и параметров влияют на то, как новые данные объединяются в существующий DataSet.

Первичные ключи

Если таблица, получающая новые данные и схему из слияния, имеет первичный ключ, новые строки из входящих данных сопоставляются с существующими строками, имеющими те же Original значения первичного ключа, что и в входящих данных. Если столбцы из входящей схемы соответствуют существующим схемам, данные в существующих строках изменяются. Столбцы, не соответствующие существующей схеме, игнорируются или добавляются в зависимости от параметра MissingSchemaAction. Новые строки со значениями первичного ключа, которые не соответствуют существующим строкам, добавляются в существующую таблицу.

Если у входящих или существующих строк состояние Added, их значения первичных ключей сопоставляются с использованием значения первичного ключа Current строки Added, поскольку версия Original строки не существует.

Если входящая таблица и существующая таблица содержат столбец с одинаковым именем, но разными типами данных, создается исключение и вызывается событие MergeFailedDataSet. Если входящая таблица и существующая таблица имеют определенные ключи, но первичные ключи предназначены для разных столбцов, создается исключение, и событие MergeFailed инициируется в DataSet.

Если таблица, получающая новые данные из слияния, не имеет первичного ключа, новые строки из входящих данных не могут быть сопоставлены с существующими строками в таблице и вместо этого добавляются в существующую таблицу.

Имена таблиц и пространства имен

DataTable объектам можно по желанию присваивать значение свойства Namespace. Когда назначаются значения Namespace, DataSet может содержать несколько объектов DataTable с одинаковым значением TableName. Во время операций слияния используются как TableName, так и Namespace для идентификации цели слияния. Если Namespace не назначен, используется только TableName для идентификации целевого объекта слияния.

Замечание

Это поведение изменилось в версии 2.0 платформы .NET Framework. В версии 1.1 пространства имен поддерживаются, но игнорируются во время операций слияния. По этой причине поведение DataSet, использующего значения свойств Namespace, будет отличаться в зависимости от версии .NET Framework, которая у вас установлена. Например, предположим, что у вас есть два DataSets, которые содержат DataTables с одинаковыми значениями свойств TableName, но разными значениями свойств Namespace. В версии 1.1 платформы .NET Framework различные Namespace имена будут игнорироваться при слиянии двух DataSet объектов. Однако, начиная с версии 2.0, слияние приводит к созданию двух новых DataTables в целевом объекте DataSet. Исходный код DataTables не влияет на слияние.

Сохранить изменения

При передаче массива DataSet, DataTable или DataRow методу Merge можно включить необязательные параметры, указывающие, следует ли сохранять изменения в существующем DataSet, и как обрабатывать новые элементы схемы, найденные в входящих данных. Первый из этих параметров после входящих данных является логическим флагом, PreserveChangesкоторый указывает, следует ли сохранять изменения в существующем DataSet. Если установлен флаг PreserveChanges, входящие значения не перезаписывают существующие значения в версии строки true существующей строки. Если для флага задано значение PreserveChanges, входящие значения перезаписывают существующие значения в версии строки false. PreserveChanges Если флаг не указан, он имеет false значение по умолчанию. Дополнительные сведения о версиях строк см. в разделе "Состояния строк" и "Версии строк".

Если PreserveChanges это true, данные из существующей строки сохраняются в Current версии существующей строки, а данные из Original версии существующей строки перезаписываются данными из Original версии строки входящей строки. Для RowState существующей строки задано значение Modified. Применяются следующие исключения:

  • Если для существующей строки установлено значение RowStateDeleted, это RowState остается Deleted и не устанавливается в значение Modified. В этом случае данные из входящей строки будут по-прежнему храниться в версии строки Original существующей строки, перезаписывая версию Original, если только входящая строка не имеет RowState значения Added.

  • Если входящая строка имеет значение RowStateAdded, данные из версии строки Original существующей строки не будут перезаписаны данными из входящей строки, так как у входящей строки нет версии строки Original.

Когда PreserveChanges равно false, обе версии строк Current и Original в существующей строке перезаписываются данными из входящей строки, а RowState существующей строки устанавливается в RowState из входящей строки. Применяются следующие исключения:

  • Если в входящей строке имеется RowStateUnchanged, и существующая строка имеет RowStateModified, Deleted или Added, то RowState существующей строки устанавливается на Modified.

  • Если входящая строка имеет RowStateAdded, а существующая строка имеет RowStateUnchanged, 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 и значением первичного ключа 1 существующая строка и Current входящая строка не считаются совпадающими, так как Original значения первичного ключа отличаются. Однако при завершении слияния и проверке ограничений возникает исключение, так как 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

См. также