47 votes

Comment nettoyer un contexte d'objet Entity Framework ?

J'ajoute plusieurs entités à un contexte d'objets.

try
{
    forach (var document in documents)
    {
        this.Validate(document); // May throw a ValidationException.

        this.objectContext.AddToDocuments(document);
    }

    this.objectContext.SaveChanges();
}
catch
{
    // How to clean-up the object context here?

    throw;
}

Si certains documents passent la validation et que l'un d'eux échoue, tous les documents qui ont passé la validation restent ajoutés au contexte de l'objet. Je dois nettoyer le contexte de l'objet car il peut être réutilisé et ce qui suit peut se produire.

var documentA = new Document { Id = 1, Data = "ValidData" };
var documentB = new Document { Id = 2, Data = "InvalidData" };
var documentC = new Document { Id = 3, Data = "ValidData" };

try
{
    // Adding document B will cause a ValidationException but only
    // after document A is added to the object context.
    this.DocumentStore.AddDocuments(new[] { documentA, documentB, documentC });
}
catch (ValidationException)
{
}

// Try again without the invalid document B. This causes an exception because
// of a duplicate primary key - document A with id 1 is added a second time.
this.DocumentStore.AddDocuments(new[] { documentA, documentC });

Cela ajoutera à nouveau le document A au contexte de l'objet et, par conséquent, le document A sera ajouté au contexte de l'objet. SaveChanges() lancera une exception à cause d'une clé primaire dupliquée.

Je dois donc supprimer tous les documents déjà ajoutés en cas d'erreur de validation. Je pourrais bien sûr effectuer la validation en premier et n'ajouter tous les documents qu'une fois qu'ils ont été validés avec succès, mais malheureusement, cela ne résout pas le problème dans son ensemble. SaveChanges() échoue, tous les documents restent ajoutés mais non sauvegardés.

J'ai essayé de détacher tous les objets retournés par

 this.objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)

mais je reçois une exception indiquant que l'objet n'est pas attaché. Alors comment puis-je me débarrasser de tous les objets ajoutés mais non sauvegardés ?

8 votes

Je recommanderais de ne pas réutiliser un objectcontext. Il y a des scénarios où cela a du sens, mais ils sont très rares. Rappelez-vous que plus un ObjectContext est utilisé longtemps, plus il est gonflé, et s'il se retrouve dans un état incohérent (c'est-à-dire si quelque chose est partiellement fait), vous pouvez aussi avoir des effets secondaires incohérents.

46voto

Garry English Points 1875

La réponse de Daniel a fonctionné pour moi, mais l'API EntityFramework est différente dans la version 6+. Voici une méthode que j'ai ajoutée à mon conteneur de dépôt personnalisé qui détachera toutes les entités du ChangeTracker du DbContext :

    /// <summary>
    /// Detaches all of the DbEntityEntry objects that have been added to the ChangeTracker.
    /// </summary>
    public void DetachAll() {

        foreach (DbEntityEntry dbEntityEntry in this.Context.ChangeTracker.Entries().ToArray()) {

            if (dbEntityEntry.Entity != null) {
                dbEntityEntry.State = EntityState.Detached;
            }
        }
    }

39voto

Daniel Brückner Points 36242

Ce n'était qu'un bogue insignifiant mais je vais laisser la question ici - peut-être que cela aidera d'autres personnes.

J'avais les éléments suivants

var objectStateEntries = this.objectContext
                             .ObjectStateManager
                             .GetObjectStateEntries(EntityState.Added);

foreach (var objectStateEntry in objectStateEntries)
{
    this.objectContext.Detach(objectStateEntry);
}

alors que je voulais ce qui suit

foreach (var objectStateEntry in objectStateEntries)
{
    this.objectContext.Detach(objectStateEntry.Entity);
}

et ne pouvait pas le voir.

8 votes

Par ailleurs, il est possible d'utiliser des drapeaux binaires pour faire var objectStateEntries = this._context.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged) ; pour détacher tous les objets attachés.

0 votes

Notez que dans certains cas, objectStateEntry.Entity sera nulle ( objectStateEntity décrit une relation avec aucune entité), donc vous devriez probablement y mettre une vérification de nullité.

2voto

d.marzo Points 31

Depuis Utilisation de EF 5.0 ChangeTracker.Clear() qui efface le DbContext de toutes les entités suivies.

1voto

var entries = ((DbContext)ef).ChangeTracker.Entries();
foreach (var entry in entries)
{
    entry.State = System.Data.EntityState.Detached;
}

1voto

arthurspa Points 1

Pour ceux qui utilisent Entity Framework Core 1.0 RC2+ (en anglais) J'ai mis à jour la réponse de Garrys.

Référence

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

Code mis à jour

        /// <summary>
        /// Detaches all of the EntityEntry objects that have been added to the ChangeTracker.
        /// </summary>
        public void DetachAll()
        {
            foreach (EntityEntry entityEntry in this.Context.ChangeTracker.Entries().ToArray())
            {
                if (entityEntry.Entity != null)
                {
                    entityEntry.State = EntityState.Detached;
                }
            }
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X