web-dev-qa-db-fra.com

Comment puis-je rejeter toutes les modifications d'un DataContext SQL vers Linq?

Sur Linq to SQL DataContext, je peux appeler SubmitChanges () pour soumettre toutes les modifications.

Ce que je veux, c'est en quelque sorte rejeter toutes les modifications du contexte de données et annuler toutes les modifications (de préférence sans passer par la base de données).

Est-ce possible?

44
Thomas Jespersen

Dans .net 3.0, utilisez db.GetChangeSet().Updates.Clear() pour les mises à jour, db.GetChangeSet().Inserts.Clear() pour les nouveaux et db.GetChangeSet().Deletes.Clear() pour les éléments supprimés.

Dans .net 3.5 et supérieur, le résultat de GetChangeSet () est désormais en lecture seule, bouclez la collection pour ou pour chaque et actualisez chaque table ChangeSet comme le fait aussi macias.

10
Alexander Zwitbaum

Pourquoi ne pas supprimer le contexte de données et le remplacer simplement par une nouvelle instance?

30
Haacked
public static class DataContextExtensions
{
    /// <summary>
    ///     Discard all pending changes of current DataContext.
    ///     All un-submitted changes, including insert/delete/modify will lost.
    /// </summary>
    /// <param name="context"></param>
    public static void DiscardPendingChanges(this DataContext context)
    {
        context.RefreshPendingChanges(RefreshMode.OverwriteCurrentValues);
        ChangeSet changeSet = context.GetChangeSet();
        if (changeSet != null)
        {
            //Undo inserts
            foreach (object objToInsert in changeSet.Inserts)
            {
                context.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
            }
            //Undo deletes
            foreach (object objToDelete in changeSet.Deletes)
            {
                context.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
            }
        }
    }

    /// <summary>
    ///     Refreshes all pending Delete/Update entity objects of current DataContext according to the specified mode.
    ///     Nothing will do on Pending Insert entity objects.
    /// </summary>
    /// <param name="context"></param>
    /// <param name="refreshMode">A value that specifies how optimistic concurrency conflicts are handled.</param>
    public static void RefreshPendingChanges(this DataContext context, RefreshMode refreshMode)
    {
        ChangeSet changeSet = context.GetChangeSet();
        if (changeSet != null)
        {
            context.Refresh(refreshMode, changeSet.Deletes);
            context.Refresh(refreshMode, changeSet.Updates);
        }
    }
}

Reportez-vous à Linq to SQL - Annuler les modifications en attente

17
Teddy

Comme Haacked l'a dit, il suffit de supprimer le contexte de données.

Vous ne devriez probablement pas garder le contexte de données en vie pendant longtemps. Ils sont conçus pour être utilisés de manière transactionnelle (c'est-à-dire un contexte de données par unité de travail atomique). Si vous maintenez un contexte de données actif pendant une longue période, vous courez un plus grand risque de générer une exception de simultanéité lorsque vous mettez à jour une entité obsolète.

9
Richard Poole

L'appel de Clear () sur la collection Updates, Deletes and Inserts ne fonctionne pas.

GetOriginalEntityState () peut être utile, mais il ne donne que les ID des relations de clé étrangère, et non les entités réelles, vous laissant ainsi un objet détaché.

Voici un article qui explique comment ignorer les modifications du contexte de données: http://graemehill.ca/discard-changes-in-linq-to-sql-datacontext

EDIT: appeler Refresh () annulera les mises à jour, mais pas les suppressions et les insertions.

9
Graeme Hill

L'actualisation fonctionnera, mais vous devrez donner les entités que vous souhaitez réinitialiser.

Par exemple

dataContext.Refresh(RefreshMode.OverwriteCurrentValues, someObject);
4
David Basarab

Vous pouvez utiliser GetOriginalEntityState (..) pour obtenir les valeurs d'origine des objets, par exemple. Clients utilisant les anciennes valeurs mises en cache.

Vous pouvez également parcourir les modifications, par exemple. met à jour et actualise les objets spécifiques uniquement et non les tables entières, car la pénalité de performance sera élevée.

foreach (Customer c in MyDBContext.GetChangeSet().Updates)
        {
            MyDBContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, c);
        }

cela annulera les modifications en utilisant des données persistantes dans la base de données.

Une autre solution consiste à supprimer le contexte de données que vous utilisez à l'aide de Dispose ().

Dans tous les cas, il est recommandé de remplacer les méthodes Insert et Remove dans la collection, par exemple. Les clients que vous utilisez et ajoutez par exemple un appel InsertOnSubmit (). Cela résoudra votre problème avec les insertions et les suppressions en attente.

3
Theodore Zographos

Mon application est de style Outlook avec une icône pour sélectionner un formulaire actif (ListBox). Avant de permettre à l'utilisateur de changer de contexte, il doit accepter les modifications ou les rejeter. 

var changes = db.GetChangeSet();
if ((changes.Updates.Count > 0) || (changes.Inserts.Count > 0) || (changes.Deletes.Count > 0))
{
    if (MessageBox.Show("Would you like to save changes?", "Save Changes", MessageBoxButton.YesNo)  == MessageBoxResult.Yes)
    {
        db.SubmitChanges();
    } else
    {
        //Rollback Changes
        foreach (object objToInsert in changes.Inserts)
        {
            db.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
        }
        foreach (object objToDelete in changes.Deletes)
        {
            db.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
        }
        foreach (object objToUpdate in changes.Updates)
        {
            db.Refresh(RefreshMode.OverwriteCurrentValues, objToUpdate);
        }
        CurrentForm.SetObject(null); //Application Code to Clear active form
        RefreshList(); //Application Code to Refresh active list
    }
}
1
Kyght

Excellente rédaction sur ici , mais voici un copier/coller du code utilisé. 

Public Sub DiscardInsertsAndDeletes(ByVal data As DataContext)
    ' Get the changes
    Dim changes = data.GetChangeSet()

    ' Delete the insertions
    For Each insertion In changes.Inserts
        data.GetTable(insertion.GetType).DeleteOnSubmit(insertion)
    Next

    ' Insert the deletions
    For Each deletion In changes.Deletes
        data.GetTable(deletion.GetType).InsertOnSubmit(deletion)
    Next
End Sub

Public Sub DiscardUpdates(ByVal data As DataContext)
    ' Get the changes
    Dim changes = data.GetChangeSet()

    ' Refresh the tables with updates
    Dim updatedTables As New List(Of ITable)
    For Each update In changes.Updates
        Dim tbl = data.GetTable(update.GetType)
        ' Make sure not to refresh the same table twice
        If updatedTables.Contains(tbl) Then
            Continue For
        Else
            updatedTables.Add(tbl)
            data.Refresh(RefreshMode.OverwriteCurrentValues, tbl)
        End If
    Next
End Sub
1
Scott

Voici comment je l'ai fait. Je viens de suivre l'exemple de Teddy ci-dessus et de le simplifier. J'ai une question cependant, pourquoi même s'embarrasser du rafraîchissement sur les DELETES? 

  public static bool UndoPendingChanges(this NtsSuiteDataContext dbContext)
  {
     if (dbContext.ChangesPending())
     {
        ChangeSet dbChangeSet = dbContext.GetChangeSet();

        dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Deletes);
        dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Updates);

        //Undo Inserts
        foreach (object objToInsert in dbChangeSet.Inserts)
        {
           dbContext.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
        }

        //Undo deletes
        foreach (object objToDelete in dbChangeSet.Deletes)
        {
           dbContext.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
        }
     }

     return true;
  }
0
M Moore