可以使用Merge方法将DataSet、DataTable或DataRow数组的内容合并到现有DataSet
中。 若干因素和选项会影响将新数据合并到现有 DataSet
中的方式。
主键
如果从合并接收新数据和架构的表具有主键,则传入数据中的新行将与现有行匹配,这些现有行具有与传入数据中的行相同的Original主键值。 如果传入架构中的列与现有架构的列匹配,则会修改现有行中的数据。 根据参数忽略或添加与现有架构不匹配的 MissingSchemaAction 列。 与任何现有行不匹配的主键值的新行将追加到现有表中。
如果传入行或现有行具有行状态 Added,则其主键值使用 Current 行的主 Added
键值进行匹配,因为不存在 Original
行版本。
如果传入表和现有表包含具有相同名称但数据类型不同的列,则会引发异常并触发 MergeFailed 的 DataSet
事件。 如果传入表和现有表都具有已定义的键,但是主键针对不同的列,将引发异常,并引发 MergeFailed
的 DataSet
事件。
如果从合并接收新数据的表没有主键,则传入数据中的新行无法与表中的现有行匹配,而是追加到现有表中。
表名称和命名空间
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
将 、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
可以使用方法的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