2010-03-17 7 views
42

Ich füge mehrere Entitäten zu einem Objektkontext hinzu.So bereinigen Sie einen Entity Framework-Objektkontext?

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

Wenn einige der Dokumente, die die Validierung vergehen und man ausfällt, werden alle Dokumente, die die Validierung bestanden bleiben dem Objektkontext hinzugefügt. Ich muss den Objektkontext aufräumen, da er wiederverwendet werden kann und Folgendes passieren kann.

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

Dies wird wieder Dokument A zum Objekt Kontext hinzufügen und in Folge SaveChanges() eine Ausnahme wegen eines doppelten Primärschlüssel werfen.

Also muss ich im Falle eines Validierungsfehlers alle bereits hinzugefügten Dokumente entfernen. Ich könnte natürlich zuerst die Validierung durchführen und nur alle Dokumente hinzufügen, nachdem sie erfolgreich validiert wurden, aber das löst leider nicht das ganze Problem - wenn SaveChanges() fehlschlägt, bleiben alle Dokumente hinzugefügt, aber nicht gespeichert.

Ich habe versucht, alle Objekte, die von

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

aber ich bin immer eine Ausnahme besagt, dass das Objekt ist nicht zurückgegeben zu lösen. Wie kann ich alle hinzugefügten, aber nicht gespeicherten Objekte entfernen?

+7

würde ich empfehlen, eine Object nicht wiederverwenden. Es gibt Szenarien, in denen es Sinn macht, aber sie sind sehr selten. Denken Sie daran, je länger ein ObjectContext verwendet wird, desto aufgeblähter wird es, und wenn es in einen inkonsistenten Zustand gerät (d. H. Etwas wird teilweise erledigt), können Sie auch einige inkonsistente Nebenwirkungen bekommen. –

Antwort

38

Es war nur ein trivialer Fehler, aber ich werde die Frage hier lassen - vielleicht hilft es anderen.

Ich hatte folgende

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

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

während ich die folgende

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

und konnte nicht sehen, es wollte.

+7

Nebenbei ist es möglich, Bitwise-Flags zu verwenden, um var objectStateEntries = this._context.ObjectStateManager. GetObjectStateEntries (EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); Alle angehängten Objekte lösen –

+0

Als Antwort markieren. –

+0

Beachten Sie, dass "objectStateEntry.Entity" in einigen Fällen null ist ("objectStateEntity" beschreibt eine Beziehung zu keiner Entität), daher sollten Sie dort wahrscheinlich einen Null-Check einfügen. –

1

Ein wenig spät auf die Party, aber haben Sie das Muster der Arbeitseinheit berücksichtigt?

Kurz gesagt, würde es ermöglichen Ihnen eine Transaktion, die Sie könnten dann (entsorgen) Rollback oder Commit (Savechanges)

NB: Das ist Transaktion bei der Gruppierung von Änderungen in einem Betrieb, nicht wie in einer SQL BEGIN TRAN. Das alles wird von der SaveChanges() -Methode für Sie erledigt.

Etwas wie:

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 

Jedes Mal, wenn Sie eine Arbeitseinheit zu erstellen, dauert es in einer Object - Wir Unity verwenden diese zu erzeugen, so dass eine neue, wenn die alte erstellt wurde entsorgt

+2

Die Verwendung einer Arbeitseinheit löst weiterhin keine Objekte aus dem Kontext. Es beantwortet auch die Frage nicht. –

+0

@AgentShark Hängt vollständig davon ab, wie Sie Ihre Rollback-Methode implementieren. Außerdem hat es keinen Sinn, den richtigen Code in der akzeptierten Antwort zu duplizieren, der explizit zeigt, wie Objekte zu lösen sind. – Basic

+0

Ich habe die Frage erneut gelesen. Ja, Updates durch eine Arbeitseinheit zu erzwingen, wäre nützlich. In EF müssen Sie jedoch vorsichtig sein, wenn Sie ein Objekt mit verwandten Objekten hinzufügen. Es gibt verschiedene Möglichkeiten mit unterschiedlichen Verhaltensweisen Objekte zum Kontext hinzuzufügen/zu speichern. –

36

Daniels Antwort funktionierte für mich, aber die EntityFramework API ist in Version 6+ anders. Hier ist eine Methode, die ich zu meinen benutzerdefinierten Repository Container hinzugefügt, das alle Objekte aus den Change des DbContext ablöst:

/// <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

funktioniert diese Methode für mich :-D danke! – duardbr

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

Für diejenigen mit Entity Framework-Core 1.0 RC2 +, ich aktualisiere Garrys' Antwort.

Referenz

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

Aktualisiert Code

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