2010-03-17 37 views
42

Estoy agregando varias entidades a un contexto de objeto.Cómo limpiar un contexto de objeto de Entity Framework?

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 algunos de los documentos pasan la validación y uno falla, todos los documentos que han pasado la validación permanecen añade al contexto objeto. Tengo que limpiar el contexto del objeto porque puede ser reutilizado y puede suceder lo siguiente.

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

Esto volverá a añadir el documento A al contexto objeto y, en consecuencia, SaveChanges() será una excepción debido a una clave principal duplicado.

Así que tengo que eliminar todos los documentos ya agregados en el caso de un error de validación. Por supuesto, pude realizar primero la validación y solo agregar todos los documentos después de que se hayan validado con éxito, pero lamentablemente esto no resuelve todo el problema: si SaveChanges() falla, todos los documentos seguirán agregados pero no guardados.

Me trataron de separar todos los objetos devueltos por

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

pero me estoy haciendo una excepción que indica que el objeto no está fijada. Entonces, ¿cómo me deshago de todos los objetos añadidos pero no guardados?

+7

Yo recomendaría no reutilizar un ObjectContext. Hay escenarios en los que tiene sentido, pero son muy raros. Recuerde que cuanto más tiempo se usa un ObjectContext, más inflado se vuelve, y si se pone en un estado incoherente (es decir, algo que se hace parcialmente) también puede tener algunos efectos secundarios inconsistentes. –

Respuesta

38

Fue solo un error trivial, pero voy a dejar la pregunta aquí - quizás ayude a otros.

que tenía la siguiente

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

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

mientras que quería la siguiente

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

y no podía verlo.

+7

Como un lado, es posible usar flags bit a bit para hacer var objectStateEntries = this._context.ObjectStateManager .GetObjectStateEntries (EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); para separar todos los objetos adjuntos –

+0

Marcarlo como respuesta. –

+0

Tenga en cuenta que en algunos casos, 'objectStateEntry.Entity' será nulo (' objectStateEntity' describe una relación con ninguna entidad), por lo que probablemente deba poner una verificación nula allí. –

1

Un poco tarde para la fiesta, pero ¿ha considerado el patrón Unidad de trabajo?

En resumen, sería permitirá tener una transacción que luego se podría deshacer (disponer) o Commit (SaveChanges)

NB: Es transacción como en la agrupación de los cambios en una sola operación, no como en un SQL COMIENZA TRAN. El método SaveChanges() aún lo maneja todo.

Algo así como:

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 

Cada vez que se crea una unidad de trabajo, que se necesita en un ObjectContext - Estamos utilizando la Unidad para generar estos para que se crea una nueva si la anterior ha sido dispuesta

+2

El uso de una unidad de trabajo aún no separa objetos del contexto. Tampoco responde la pregunta. –

+0

@AgentShark Depende completamente de cómo implemente su método 'Rollback'. Además, no tiene sentido duplicar el código correcto en la respuesta aceptada que muestra explícitamente cómo separar objetos. – Basic

+0

He vuelto a leer la pregunta. Sí, forzar actualizaciones a través de una unidad de trabajo sería útil. Sin embargo, en EF, debe tener cuidado al agregar objetos con entidades relacionadas. Existen diferentes formas de comportamientos diferentes para agregar/guardar objetos al contexto. –

36

La respuesta de Daniel funcionó para mí, sin embargo, la API de EntityFramework es diferente en la versión 6+. Aquí es un método añadí a mi contenedor repositorio personalizado que va a separar todas las entidades de ChangeTracker del 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

este método funciona para mí :-D gracias! – duardbr

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

Para aquellos que utilizan Entity Framework Core 1.0 RC2 +, he actualizado respuesta garrys'.

Referencia

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

código actualizado

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