2010-03-17 3 views
42

J'ajoute plusieurs entités à un contexte d'objet. Si certains documents réussissent la validation et qu'un échec échoue, tous les documents ayant 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 arriver.Comment nettoyer un contexte d'objet Entity Framework?

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 }); 

Ce sera de nouveau ajouter le document A au contexte de l'objet et par conséquent SaveChanges() va lancer une exception en raison d'une clé primaire en double.

Je dois donc supprimer tous les documents déjà ajoutés dans le cas d'une erreur de validation. Je pourrais bien sûr effectuer la validation en premier et ajouter seulement tous les documents après qu'ils aient été validés avec succès, mais malheureusement cela ne résout pas tout le problème - si SaveChanges() échoue, tous les documents restent encore 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 sauvés?

+7

Je ne recommanderais pas réutiliser un ObjectContext. Il y a des scénarios où cela a du sens, mais ils sont très rares. Souvenez-vous que plus un objet ObjectContext est utilisé, plus il est gonflé, et s'il entre dans un état incohérent (c'est-à-dire que quelque chose est partiellement fait), vous risquez également d'avoir des effets secondaires incohérents. –

Répondre

38

C'était juste un bug trivial mais je vais laisser la question ici - peut-être que ça aide les autres.

J'ai eu l'

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

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

alors que je voulais la

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

et ne pouvait pas le voir.

+7

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

+0

Marquer comme réponse. –

+0

Notez que dans certains cas, 'objectStateEntry.Entity' sera nul (' objectStateEntity' décrit une relation à aucune entité), vous devriez donc probablement mettre une valeur nulle dans ce champ. –

1

Un peu en retard à la fête, mais avez-vous considéré le motif de l'unité de travail?

En bref, il vous permettra d'avoir une transaction que vous pouvez ensuite rollback (éliminer) ou engager (savechanges)

NB: C'est la transaction comme regroupement de changements en une seule opération, non pas comme dans un sql COMMENCER TRAN. Tout cela est toujours géré pour vous par la méthode SaveChanges().

Quelque chose comme:

Public Class UnitOfWork 
    Implements IUnitOfWork 
    Implements IDisposable 
    Private ReadOnly Property ObjectContext As Interfaces.IObjectContext 

    Public Sub New(ByVal objectContext As Interfaces.IObjectContext) 
     _objectContext = objectContext 
    End Sub 

    Public Sub Dispose() Implements IDisposable.Dispose 
     If _objectContext IsNot Nothing Then 
      _objectContext.Dispose() 
     End If 
     GC.SuppressFinalize(Me) 
    End Sub 

    Public Sub Commit() Implements IUnitOfWork.Commit 
     _objectContext.SaveChanges() 
    End Sub 
End Class 

Chaque fois que vous créez une unité de travail, il faut dans un ObjectContext - Nous utilisons l'unité pour générer ces soi qu'un nouveau est créé si l'ancien a été

+2

L'utilisation d'une unité de travail ne détache toujours pas les objets du contexte. Il ne répond pas non plus à la question. –

+0

@AgentShark Dépend entièrement de la façon dont vous implémentez votre méthode 'Rollback'. En outre, il est inutile de dupliquer le bon code dans la réponse acceptée qui montre explicitement comment détacher les objets – Basic

+0

Je relis la question. Oui, forcer les mises à jour à travers une unité de travail serait utile. Cependant, dans EF, vous devez faire attention lorsque vous ajoutez un objet avec des entités connexes. Il existe différentes manières avec différents comportements d'ajouter/sauvegarder des objets au contexte. –

36

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'ajouté à mon conteneur de référentiel personnalisé qui détachez toutes les entités de la 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()) { 

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

cette méthode fonctionne pour moi :-D merci! – duardbr

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

Pour ceux qui utilisent Entity Framework de base 1.0 RC2 +, je 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; 
       } 
      } 
     } 
Questions connexes