Unione di contenuti di dataset

È possibile usare il metodo Merge per unire il contenuto di un oggetto DataSet, DataTable o una matrice DataRow in un DataSet esistente. Il merge di nuovi dati in un DataSet esistente è influenzato da diversi fattori e opzioni.

Chiavi primarie

Se la tabella che riceve i nuovi dati e lo schema da un'operazione di merge dispone di una chiave primaria, le nuove righe dei dati in arrivo verranno associate alle righe esistenti i cui valori di chiave primaria Original corrispondono a quelli presenti nei dati in arrivo. Se le colonne dello schema in arrivo corrispondono alle colonne dello schema esistente, i dati delle righe esistenti verranno modificati. Le colonne che non corrispondono allo schema esistente verranno ignorate o aggiunte in base al parametro MissingSchemaAction. Le nuove righe contenenti valori di chiave primaria non corrispondenti a nessuna delle righe esistenti verranno aggiunte alla tabella esistente.

Se alle righe in arrivo o esistenti è associato uno stato di riga Added, i relativi valori di chiave primaria verranno associati tramite il valore di chiave primaria Current della riga Added, poiché non è presente nessuna versione di riga Original.

Se in una tabella in arrivo e una tabella esistente è contenuta una colonna con lo stesso nome ma con tipi di dati diversi, verranno generati un'eccezione e l'evento MergeFailed del DataSet. Se nella tabella in arrivo e nella tabella esistente sono state definite chiavi, ma le chiavi primarie sono relative a colonne diverse, verranno generati un'eccezione e l'evento MergeFailed del DataSet.

Se la tabella che riceve i nuovi dati da un'operazione di unione non dispone di alcuna chiave primaria, non sarà possibile associare i nuovi valori relativi ai dati in arrivo alle righe esistenti della tabella. Tali valori verranno quindi aggiunti alla tabella esistente.

Nomi di tabella e spazi dei nomi

Se necessario, agli oggetti DataTable è possibile assegnare un valore della proprietà Namespace. Quando vengono assegnati i valori di Namespace, un oggetto DataSet può contenere più oggetti DataTable con lo stesso valore TableName. Durante le operazioni di merge vengono usati TableName e Namespace per identificare la destinazione di un merge. Se è stato assegnato Namespace, per identificare la destinazione di un'unione viene usato TableName.

Nota

Questo comportamento è stato modificato in .NET Framework versione 2.0. Nella versione 1.1 gli spazi dei nomi sono supportati ma vengono ignorati durante le operazioni di merge. Per questo motivo il comportamento di un oggetto DataSet che usa i valori della proprietà Namespace a seconda della versione di .NET Framework in esecuzione. Ad esempio, si supponga di disporre di due DataSets contenenti DataTables con lo stesso valore della proprietà TableName ma valori diversi della proprietà Namespace. Nella versione 1.1 di .NET Framework, i diversi nomi di Namespace verranno ignorati durante l'unione dei due oggetti DataSet. A partire dalla versione 2.0, invece, in seguito all'unione vengono creati due nuovi oggetti DataTables nell'oggetto DataSet di destinazione. I DataTables originali non verranno interessati dall'unione.

PreserveChanges

Quando si passa una matrice DataSet, DataTable o DataRow al metodo Merge, è possibile includere parametri facoltativi che consentano di specificare se mantenere o meno le modifiche nel DataSet esistente e come gestire i nuovi elementi dello schema individuati nei dati in arrivo. Il primo di tali parametri successivi ai dati in arrivo è un flag booleano, PreserveChanges, che consente di specificare se conservare o meno le modifiche nel DataSet esistente. Se il flag PreserveChanges è impostato su true, i valori esistenti nella versione di riga Current della riga esistente non verranno sovrascritti dai valori in arrivo. Se il flag PreserveChanges è impostato su false, i valori esistenti nella versione di riga Current della riga esistente verranno sovrascritti dai valori in arrivo. Se non è specificato, il flag PreserveChanges viene impostato su false per impostazione predefinita. Per altre informazioni sulle versioni delle righe, vedere Stati e versioni delle righe.

Se PreserveChanges è true, i dati della riga esistente verranno mantenuti nella versione di riga Current della riga esistente, mentre i dati della versione di riga Original della riga esistente verranno sovrascritti dai dati della versione di riga Original della riga in arrivo. La proprietà RowState della riga esistente è impostata su Modified. Vengono applicate le seguenti eccezioni:

  • Se il valore di RowState per la riga esistente è Deleted, il valore di RowState rimane Deleted e non viene impostato su Modified. In questo caso i dati contenuti nella riga in arrivo verranno archiviati comunque nella versione di riga Original della riga esistente, sovrascrivendo la versione di riga Original della riga esistente (a meno che il valore di RowState per la riga in arrivo non sia Added).

  • Se il valore di RowState per la riga in arrivo è Added, i dati della versione di riga Original della riga esistente non verranno sovrascritti con i dati della riga in arrivo, poiché a tale riga non è associata nessuna versione di riga Original.

Se PreserveChanges è false, le versioni di riga Current e Original della riga esistente verranno sovrascritte con i dati della riga in arrivo e il valore di RowState della riga esistente verrà impostato sul valore di RowState della riga in arrivo. Vengono applicate le seguenti eccezioni:

  • Se il valore di RowState per la riga in arrivo è Unchanged e il valore di RowState per la riga esistente è Modified, Deletedo Added, il valore di RowState per la riga esistente verrà impostato su Modified.

  • Se il valore di RowState per la riga in arrivo è Added e il valore di RowState per la riga esistente è Unchanged, Modified o Deleted, il valore di RowState per la riga esistente verrà impostato su Modified. I dati contenuti nella versione di riga Original della riga esistente non verranno inoltre sovrascritti con i dati della riga in arrivo, poiché a tale riga non è associata nessuna versione di riga Original.

MissingSchemaAction

È possibile usare il parametro facoltativo MissingSchemaAction del metodo Merge per specificare in che modo Merge gestirà gli elementi dello schema nei dati in arrivo che non fanno parte del DataSet esistente.

Nella tabella seguente vengono descritte le opzioni per MissingSchemaAction.

Opzione MissingSchemaAction Descrizione
Add Aggiunge le nuove informazioni dello schema al DataSet e popola le nuove colonne usando i valori in arrivo. Si tratta dell'impostazione predefinita.
AddWithKey Aggiunge le nuove informazioni sullo schema e sulla chiave primaria al DataSet e popola le nuove colonne usando i valori in arrivo.
Error Genera un'eccezione nel caso in cui vengano rilevate delle informazioni relative allo schema non associate correttamente.
Ignore Ignora le nuove informazioni relative allo schema.

Vincoli

Se si usa il metodo Merge, la verifica dei vincoli viene eseguita solo quando tutti i nuovi dati sono stati aggiunti al DataSet esistente. Una volta aggiunti i dati, i vincoli vengono applicati ai valori correnti del DataSet. È necessario assicurarsi che il codice sia in grado di gestire eventuali eccezioni generate a causa di violazioni dei vincoli.

Si consideri il caso in cui una riga esistente di un DataSet è una riga Unchanged con un valore di chiave primaria di 1. Durante un'operazione di unione con una riga in arrivo Modified in cui il valore di chiave primaria Original è 2 il valore di chiave primaria Current è 1, la riga esistente e la riga in arrivo non vengono considerate corrispondenti perché i valori della chiave primaria di Original sono diversi. Tuttavia, una volta completata l'unione e verificati i vincoli, verrà generata un'eccezione, poiché i valori di chiave primaria Current violano il vincolo univoco per la colonna di chiave primaria.

Nota

Quando si inseriscono righe in una tabella di database contenente una colonna a incremento automatico ad esempio una colonna Identity, è possibile che il valore della colonna Identity restituito dall'inserimento non corrisponda al valore di DataSet, pertanto le righe restituite verranno aggiunte anziché unite. Per altre informazioni, vedere Recupero di valori di identità o di numerazione automatica.

Nell'esempio di codice seguente viene eseguito il merge di due oggetti DataSet con schemi diversi in un unico DataSet contenente una combinazione degli schemi dei due oggetti DataSet in arrivo.

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

Nell'esempio di codice seguente viene selezionato un DataSet esistente con aggiornamenti e tali aggiornamenti vengono passati a un DataAdapter per l'elaborazione nell'origine dati. I risultati vengono quindi uniti nel DataSet originale. Dopo aver rifiutato le modifiche che causavano un errore, le modifiche unite vengono confermate tramite 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

Vedi anche