Слияние содержимого набора данных
Метод 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