Fusion de contenu de DataSet

Vous pouvez utiliser la méthode Merge pour fusionner le contenu d'un tableau DataSet, DataTable ou DataRow dans un DataSet existant. La façon dont les nouvelles données sont fusionnées dans un DataSet existant dépend de plusieurs facteurs et options.

Clés primaires

Si la table qui reçoit les nouvelles données et informations de schéma suite à une fusion dispose d’une clé primaire, les nouvelles lignes des données entrantes sont mises en correspondance avec les lignes existantes ayant les mêmes valeurs de clé primaire Original que celles des données entrantes. Si les colonnes du schéma entrant correspondent à celles du schéma existant, les données des lignes existantes sont modifiées. Les colonnes qui ne correspondent pas au schéma existant sont soit ignorées soit ajoutées en fonction du paramètre MissingSchemaAction. Les nouvelles lignes dont les valeurs de clé primaire ne correspondent à aucune ligne existante sont ajoutées à la table existante.

Si des lignes entrantes ou existantes présentent un état de ligne Added, leurs valeurs de clé primaire sont mises en correspondance à l'aide de la valeur de clé primaire Current de la ligne Added car il n'existe pas de version Original de ces lignes.

Si une table entrante et une table existante contiennent une colonne du même nom, mais dont le type de données diffère, une exception est levée et l'événement MergeFailed du DataSet est déclenché. Si une table entrante et une table existante contiennent toutes deux des clés définies, mais si les clés primaires s'appliquent à des colonnes différentes, une exception est levée et l'événement MergeFailed du DataSet est déclenché.

Si aucune clé primaire n'est définie dans la table qui reçoit les nouvelles données suite à une fusion, les nouvelles lignes des données entrantes ne peuvent pas être mises en correspondance avec les lignes existantes de la table et sont alors ajoutées à la table existante.

Noms de tables et espaces de noms

Une valeur de propriété DataTable peut être assignée à un objet Namespace. Lorsque des valeurs de propriété Namespace sont assignées, un DataSet peut contenir plusieurs objets DataTable avec la même valeur de propriété TableName. Au cours des opérations de fusion, les propriétés TableName et Namespace sont toutes deux utilisées pour identifier la cible d'une fusion. Si aucune propriété Namespace n’a été assignée, seule la propriété TableName est utilisée pour identifier la cible d’une fusion.

Notes

Ce comportement est modifié dans la version 2.0 du .NET Framework. Dans la version 1.1, les espaces de noms étaient pris en charge mais étaient ignorés au cours des opérations de fusion. C'est pourquoi, un objet DataSet qui utilise les valeurs de propriété Namespace aura des comportements différents selon la version du .NET Framework qui est exécutée. Prenons par exemple deux DataSets contenant des DataTables avec les mêmes valeurs de propriété TableName mais des valeurs de propriété Namespace différentes. Dans la version 1.1 du .NET Framework, les noms Namespace différents seront ignorés lors de la fusion de deux objets DataSet. Toutefois, à partir de la version 2.0, la fusion entraîne la création de deux DataTables dans le DataSet cible. Les DataTables d'origine ne seront pas affectés par la fusion.

PreserveChanges

Lorsque vous passez un tableau DataSet, DataTable ou DataRow à la méthode Merge, vous pouvez inclure des paramètres facultatifs pour indiquer s'il convient ou non de conserver les modifications dans le DataSet existant et comment gérer les nouveaux éléments de schéma situés dans les données entrantes. Le premier de ces paramètres après les données entrantes est un indicateur booléen, PreserveChanges, qui indique si les modifications seront ou non conservées dans le DataSet existant. Si l'indicateur PreserveChanges a la valeur true, les valeurs entrantes ne remplacent pas les valeurs existantes dans la version Current de la ligne existante. Si l'indicateur PreserveChanges a la valeur false, les valeurs entrantes remplacent les valeurs existantes dans la version Current de la ligne existante. Si l'indicateur PreserveChanges n'est pas spécifié, il a par défaut la valeur false. Pour plus d’informations sur les versions de ligne, consultez États de ligne et versions de ligne.

Lorsque PreserveChanges a la valeur true, les données de la ligne existante sont conservées dans la version Current de la ligne existante, alors que les données de la version Original de la ligne existante sont remplacées par les données de la version Original de la ligne entrante. La propriété RowState de la ligne existante prend la valeur Modified. Il existe toutefois certaines exceptions :

  • Si le RowState de la ligne existante est Deleted, ce RowState reste Deleted et ne prend pas la valeur Modified. Dans ce cas, les données de la ligne entrante seront stockées dans la version Original de la ligne existante, remplaçant ainsi la version Original de la ligne existante (sauf si le RowState de la ligne entrante est Added).

  • Si le RowState de la ligne entrante est Added, les données de la version Original de la ligne existante ne seront pas remplacées par les données de la ligne entrante car cette dernière n'a pas de version de ligne Original.

Lorsque PreserveChanges a la valeur false, les versions Current et Original de ligne existante sont remplacées par les données de la ligne entrante et le RowState de la ligne existante prend la valeur du RowState de la ligne entrante. Il existe toutefois certaines exceptions :

  • Si le RowState de la ligne entrante est Unchanged et si le RowState de la ligne existante est Modified, Deleted ou Added, le RowState de la ligne existante prend la valeur Modified.

  • Si le RowState de la ligne entrante est Added et si le RowState de la ligne existante est Unchanged, Modified ou Deleted, le RowState de la ligne existante prend la valeur Modified. De même, les données de la version Original de la ligne existante ne sont pas remplacées par les données de la ligne entrante car cette dernière n'a pas de version de ligne Original.

MissingSchemaAction

Vous pouvez utiliser le paramètre facultatif MissingSchemaAction de la méthode Merge pour spécifier comment Merge va gérer les éléments de schéma présents dans les données entrantes et qui ne font pas partie du DataSet existant.

Le tableau suivant décrit les options de MissingSchemaAction.

Option MissingSchemaAction Description
Add Ajoute les nouvelles informations de schéma au DataSet et remplit les nouvelles colonnes avec les valeurs entrantes. Il s’agit de la valeur par défaut.
AddWithKey Ajoute les nouvelles informations de schéma et de clé primaire au DataSet et remplit les nouvelles colonnes avec les valeurs entrantes.
Error Lève une exception si une incompatibilité au niveau des informations de schéma est détectée.
Ignore Ignore les nouvelles informations de schéma.

Contraintes

Avec la méthodeMerge, la vérification des contraintes ne s'effectue qu'une fois toutes les nouvelles données ajoutées au DataSet existant. Les contraintes sont alors appliquées aux valeurs actuelles du DataSet. Vous devez veiller à ce que votre code gère les exceptions levées en cas de violations des contraintes.

Prenons l'exemple d'une ligne DataSet existante d'un Unchanged dont la valeur de clé primaire est 1. Au cours d'une opération de fusion avec une ligne Modified entrante dont la valeur de clé primaire Original est 2 et la valeur de clé primaire Current est 1, la ligne existante et la ligne entrante ne sont pas mises en correspondance car les valeurs de clé primaire Original diffèrent. Cependant, lorsque la fusion est terminée et la vérification des contraintes effectuée, une exception est levée car les valeurs de clé primaire Current enfreignent la contrainte unique définie pour la colonne de clé primaire.

Notes

Lorsque des lignes sont insérées dans une table de base de données contenant une colonne à incrémentation automatique comme une colonne d’identité, la valeur de colonne d’identité retournée par l’insertion peut ne pas correspondre à la valeur du DataSet, ce qui conduit à l’ajout des lignes retournées et non à leur fusion. Pour plus d’informations, consultez Extraction de l’identité ou de valeurs à numérotation automatique.

L’exemple de code suivant fusionne deux objets DataSet contenant des schémas différents pour donner un DataSet dans lequel les schémas des deux objets DataSet entrants sont combinés.

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

L'exemple de code suivant prend un DataSet existant avec des mises à jour et passe ces mises à jour à un DataAdapter pour qu'elles soient traitées au niveau de la source de données. Les résultats sont ensuite fusionnés dans le DataSet d’origine. Après rejet des modifications ayant abouti à une erreur, les modifications fusionnées sont validées avec 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

Voir aussi