Слияние содержимого набора данных
Метод 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. Применяются следующие исключения:
Если существующая строка имеет версию
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 | 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