Combinar contenido de DataSet

Se puede utilizar el método Merge para combinar el contenido de DataSet, DataTable o matriz de DataRow en un DataSet existente. Hay varios factores y opciones que afectan a cómo se combinan los datos nuevos en un DataSet existente.

Claves principales

Si la tabla que recibe datos y esquema nuevos a partir de una fusión mediante combinación tiene una clave principal, las nuevas filas de los datos entrantes se hacen coincidir con las filas existentes que tienen los mismos valores de clave principal Original que los de los datos entrantes. Si las columnas del esquema entrante coinciden con las del esquema existente, se modificarán los datos de las filas existentes. Las columnas que no coincidan con el esquema existente se pasarán por alto o se agregarán en función del parámetro MissingSchemaAction. Las nuevas filas con valores de clave principal que no coincidan con las filas existentes se agregarán a la tabla existente.

Si las filas entrantes o las existentes tienen un estado Added, se harán coincidir sus valores de clave principal mediante el valor de clave principal Current de la fila Added, ya que no existe ninguna versión de fila Original.

Si una tabla entrante y una tabla existente tienen una columna con el mismo nombre pero con distintos tipos de datos, se iniciará una excepción y se generará el evento MergeFailed de DataSet. Si una tabla entrante y una tabla existente tienen claves definidas, pero las claves principales corresponden a columnas diferentes, se iniciará una excepción y se provocará el evento MergeFailed de DataSet.

Si la tabla que recibe nuevos datos de una combinación no tiene una clave principal, las nuevas filas de los datos entrantes no se pueden hacer coincidir con las filas existentes de la tabla y se agregarán a la tabla existente.

Nombres de tabla y espacios de nombres

A los objetos DataTable se les puede asignarse también un valor de propiedad Namespace. Cuando se asignan los valores Namespace, DataSet puede contener varios objetos DataTable con el mismo valor TableName. Durante las operaciones de fusión mediante combinación, se utilizan tanto TableName como Namespace para identificar el destino de una fusión mediante combinación. Si no se ha asignado Namespace solo se utiliza TableName para identificar el destino de una fusión mediante combinación.

Nota

Este comportamiento ha cambiado en la versión 2.0 de .NET Framework. En la versión 1.1, se admitían los espacios de nombres pero eran pasados por alto durante las operaciones de fusión mediante combinación. Por ello, un DataSet que utiliza valores de propiedad Namespace tendrá diferentes comportamientos en función de la versión de .NET Framework que se ejecute. Por ejemplo, suponga que tiene dos DataSets que contienen DataTables con los mismos valores de propiedad TableName pero distintos valores de propiedad Namespace. En la versión 1.1 de .NET Framework, los nombres Namespace distintos serán pasados por alto cuando se combinen dos objetos DataSet. Sin embargo, a partir de la versión 2.0 de .NET Framework, la combinación produce dos nuevos DataTables para crearse en el DataSet de destino. El DataTables original no se verá afectado por la combinación.

PreserveChanges

Cuando se pasa una matriz de DataSet, DataTable o DataRow al método Merge, es posible incluir parámetros opcionales que especifiquen si se conservarán o no los cambios en el DataSet existente y cómo tratar los nuevos elementos de esquema de los datos entrantes. El primero de estos parámetros después de los datos entrantes es una marca booleana, PreserveChanges, que especifica si se conservarán o no los cambios en el DataSet existente. Si la marca PreserveChanges está establecida en true, los valores entrantes no sobrescriben los existentes en la versión de fila Current de la fila existente. Si la marca PreserveChanges está establecida en false, los valores entrantes sobrescriben los existentes en la versión de fila Current de la fila existente. Si la marca PreserveChanges no está especificado, de forma predeterminada se establece en false. Para obtener más información sobre estados y versiones de fila, vea Estados de fila y Versiones de fila.

Cuando PreserveChanges es true, los datos de la fila existente se mantienen en la versión de fila Current de la fila existente, mientras que los datos de la versión de fila Original de la fila existente se sobrescriben con los datos de la versión de fila Original de la fila entrante. El RowState de la fila existente se establece en Modified. Se aplican las excepciones siguientes:

  • Si la fila existente tiene un RowState de Deleted, RowState se mantiene en Deleted y no se establece en Modified. En este caso, los datos de la fila entrante se almacenarán en la versión de fila Original de la fila existente, sobrescribiendo la versión de fila Original de la fila existente (a menos que la fila entrante tenga un RowState de Added).

  • Si la fila entrante tiene un RowState de Added, los datos de la versión de fila Original de la fila existente no se sobrescribirán con datos de la fila entrante, ya que ésta no tiene una versión de fila Original.

Cuando PreserveChanges es false, las versiones de fila Current y Original de la fila existente se sobrescriben con datos de la fila entrante y el RowState de la fila existente se establece como el RowState de la fila entrante. Se aplican las excepciones siguientes:

  • Si la fila entrante tiene un RowState de Unchanged y la fila existente tiene un RowState de Modified, Deleted o Added, el RowState de la fila existente se establece en Modified.

  • Si la fila entrante tiene un RowState de Added y la fila existente tiene un RowState de Unchanged, Modified o Deleted, el RowState de la fila existente se establece en Modified. Además, los datos de la versión de fila Original de la fila existente no se sobrescriben con datos de la fila entrante, ya que ésta no tiene una versión de fila Original.

MissingSchemaAction

Es posible utilizar el parámetro opcional MissingSchemaAction del método Merge para especificar cómo tratará Merge los elementos del esquema de los datos entrantes que no formen parte del DataSet existente.

En la siguiente tabla se describen las opciones de MissingSchemaAction.

Opción MissingSchemaAction Descripción
Add Agrega al DataSet la nueva información de esquema y rellena las nuevas columnas con los valores entrantes. Este es el valor predeterminado.
AddWithKey Agrega al DataSet la nueva información de esquema y de clave principal y rellena las nuevas columnas con los valores entrantes.
Error Inicia una excepción si se encuentra información de esquema no coincidente.
Ignore Pasa por alto la nueva información de esquema.

Restricciones

Con el método Merge no se comprueban las restricciones hasta que se agregan todos los datos nuevos al DataSet existente. Una vez agregados los datos, se aplican restricciones en los valores actuales del DataSet. Hay que asegurarse de que el código controla las excepciones que puedan iniciarse debido a infracciones de las restricciones.

Tomemos como ejemplo un caso en el que una fila existente de un DataSet es una fila Unchanged con un valor de clave principal de 1. Durante una operación de combinación con una fila entrante Modified cuyo valor de clave principal Original es 2 y de clave principal Current es 1, la fila existente y la fila entrante no se consideran coincidentes porque los valores de clave principal Original son diferentes. Sin embargo, cuando se completa la combinación y se comprueban las restricciones, se iniciará una excepción porque los valores de clave principal Current infringen la restricción única de la columna de clave principal.

Nota

Cuando las filas están insertadas en una base de datos con columnas de incremento automático como puede ser una columna de identidad, el valor de columna de identidad devuelto por la inserción no coincide con el valor de DataSet, lo que da lugar a que las filas devueltas se agreguen en lugar de combinarse. Para obtener más información, consulte Recuperación de valores de identidad o autonumeración.

En el siguiente ejemplo de código se combinan dos objetos DataSet con esquemas diferentes en un DataSet con los esquemas combinados de los dos objetos DataSet entrantes.

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

En el siguiente ejemplo de código se toma un DataSet existente con actualizaciones y se pasan esas actualizaciones a un DataAdapter para que se procesen en el origen de datos. Los resultados se combinan entonces en el DataSet original. Después de rechazar los cambios que produjeron un error, se confirman los cambios combinados con 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

Consulte también