共用方式為


合併資料集內容

您可以使用 Merge 方法,將 DataSetDataTableDataRow 陣列的內容合併至現有的 DataSet 中。 有許多因素和選項會影響新資料合併至現有 DataSet 的方式。

主索引鍵

如果從合併接收新資料和結構描述的資料表有主索引鍵,則來自內送資料的新資料列會與現有資料列 (與內送資料中的資料列具有相同的 Original 主索引鍵值) 進行比對。 如果來自內送結構描述的資料行與現有結構描述相符,則會修改現有資料列內的資料。 系統會根據 MissingSchemaAction 參數來忽略或加入與現有結構描述不符的資料行。 主索引鍵值不符合任何現有資料列的新資料列會附加至現有的資料表。

如果內送或現有資料列的資料列狀態為 Added,系統就會使用 Current 資料列的 Added 主索引鍵值來比對其主索引鍵值,因為沒有任何 Original 資料列版本存在。

如果內送資料表和現有資料表包含名稱相同但資料型別不同的資料行,則會擲回例外狀況,而且會引發 MergeFailedDataSet 事件。 如果內送資料表和現有資料表都已定義索引鍵,但主索引鍵用於不同的資料行,則會擲回例外狀況,而且會引發 MergeFailedDataSet 事件。

如果在合併時接收新資料的資料表沒有主索引鍵,則來自內送資料的新資料列將無法與資料表中的現有資料列對應,反而會附加至現有資料表。

資料表名稱和命名空間

您可以選擇性地指派 DataTable 屬性值給 Namespace 物件。 當您指派 Namespace 值時,DataSet 可以包含多個具有相同 DataTable 值的 TableName 物件。 進行合併作業期間,TableNameNamespace 都會用於識別合併的目標。 如果沒有指派任何 Namespace,系統就只會使用 TableName 來識別合併的目標。

注意

這個行為在 .NET Framework 2.0 版中已變更。 在 1.1 版中,雖然系統支援命名空間 (Namespace),但是會在合併作業期間忽略命名空間。 因此,使用 DataSet 屬性值的 Namespace 將根據您正在執行的 .NET Framework 版本而具有不同的行為。 例如,假設您有兩個 DataSets,其中包含 DataTables 屬性值相同但 TableName 屬性值不同的 Namespace。 在 .NET Framework 1.1 版中,當您合併這兩個 Namespace 物件時,系統會忽略不同的 DataSet 名稱。 不過,從 2.0 版開始,合併會導致系統在目標 DataTables 中建立兩個新的 DataSet。 原始 DataTables 將不會受到合併的影響。

PreserveChanges

當您將 DataSetDataTableDataRow 陣列傳遞給 Merge 方法時,可以包含選擇性參數來指定是否要保留現有 DataSet 的變更,以及如何處理內送資料中發現的新結構描述項目。 內送資料後續參數中的第一個參數是布林值 (Boolean) 旗標 PreserveChanges,它可指定是否要保留現有 DataSet 的變更。 如果 PreserveChanges 旗標設定為 true,內送值就不會覆寫現有資料列之 Current 資料列版本中現有的值。 如果 PreserveChanges 旗標設定為 false,內送值就會覆寫現有資料列之 Current 資料列版本中現有的值。 如果您沒有指定 PreserveChanges 旗標,它預設會設定為 false。 如需資料列版本的詳細資訊,請參閱資料列狀態與資料列版本

PreserveChangestrue 時,現有資料列的資料會保留在現有資料列的 Current 資料列版本中,而現有資料列之 Original 資料列版本的資料則會以內送資料列之 Original 資料列版本的資料加以覆寫。 現有資料列的 RowState 會設定為 Modified。 下列情況則例外:

  • 如果現有資料列的 RowStateDeleted,則這個 RowState 會維持 Deleted 而不會設定為 Modified。 在這種情況下,內送資料列的資料仍然會儲存在現有資料列的 Original 資料列版本中,並覆寫現有資料列的 Original 資料列版本 (除非內送資料列的 RowStateAdded)。

  • 如果內送資料列的 RowStateAdded,現有資料列之 Original 資料列版本的資料將不會以內送資料列的資料加以覆寫,因為內送資料列沒有 Original 資料列版本。

PreserveChangesfalse 時,現有資料列中的 CurrentOriginal 資料列版本都會以內送資料列的資料加以覆寫,而且現有資料列的 RowState 會設定為內送資料列的 RowState。 下列情況則例外:

  • 如果內送資料列的 RowStateUnchanged 而且現有資料列的 RowStateModifiedDeletedAdded,則現有資料列的 RowState 會設定為 Modified

  • 如果內送資料列的 RowStateAdded,而且現有資料列的 RowStateUnchangedModifiedDeleted,則現有資料列的 RowState 會設定為 Modified。 此外,現有資料列之 Original 資料列版本的資料不會以內送資料列的資料加以覆寫,因為內送資料列沒有 Original 資料列版本。

MissingSchemaAction

您可以使用 MissingSchemaAction 方法的選擇性 Merge 參數來指定 Merge 該如何處理不屬於現有 DataSet 之內送資料中的結構描述項目。

下表將說明 MissingSchemaAction 的選項。

MissingSchemaAction 選項 描述
Add 將新的結構描述資訊加入至 DataSet,並將內送值填入新資料行。 這是預設值。
AddWithKey 將新的結構描述和主索引鍵資訊加入至 DataSet,並將內送值填入新資料行。
Error 若有不相符的結構描述資訊,則會發生例外狀況。
Ignore 忽略新的結構描述資訊。

條件約束

使用 Merge 方法時,系統要等到所有的新資料都已經加入至現有 DataSet 之後,才會檢查條件約束。 一旦資料加入後,條件約束會強制執行於 DataSet 中目前的值。 您必須確保程式碼可處理任何因條件約束違規而造成的例外狀況。

假設 DataSet 中的現有資料列是主索引鍵值為 1 的 Unchanged 資料列。 當它與 Modified 主索引鍵值為 2 而 Original 主索引鍵值為 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

另請參閱