Слияние содержимого набора данных

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

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

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

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

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

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

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

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

Примечание.

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

Флаг PreserveChanges

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

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

  • Если существующая строка имеет версию RowStateDeleted, это 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 Description
Add Добавление новых данных схемы в объект DataSet и заполнение новых столбцов входными значениями. Это значение по умолчанию.
AddWithKey Добавление новых данных схемы и первичного ключа в объект DataSet и заполнение новых столбцов входными значениями.
Error Инициация исключения, если встречаются несовпадающие данные схемы.
Ignore Пропуск новых данных схемы.

Ограничения

При выполнении метода Merge ограничения не проверяются, пока все новые данные не будут добавлены в существующий объект DataSet. После добавления данных ограничения принудительно применяются на текущих значениях в объекте DataSet. Необходимо быть уверенным в том, что код обрабатывает все исключения, которые могут инициироваться из-за нарушений ограничений.

Рассмотрим случай, когда существующая строка в DataSet является строкой Unchanged со значением первичного ключа, равным 1. Во время выполнения операции слияния с входной строкой Modified со значением первичного ключа Original, равным 2, и значением первичного ключа Current, равным 1, существующая строка и входная строка не считаются совпадающими, так как различаются значения первичных ключей 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

См. также