2010-03-17 5 views
42

Я добавляю несколько объектов в контекст объекта.Как очистить контекст объекта 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; 
} 

Если некоторые документы проходят проверку и один выходит из строя, все документы, прошедшие проверку остаются добавлены к контексту объекта. Я должен очистить контекст объекта, потому что он может быть повторно использован, и может случиться следующее.

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

Это снова добавить документ А к контексту объекта и в результате SaveChanges() сгенерирует исключение из дубликата первичного ключа.

Так что я должен удалить все уже добавленные документы в случае ошибки проверки. Я мог бы, конечно, выполнить проверку сначала и добавить все документы только после того, как они были успешно проверены, но, к сожалению, это не решает всей проблемы - если SaveChanges() не удается, все документы остаются добавленными, но несохраненными.

Я попытался отключить все объекты, возвращенные

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

, но я получаю исключение о том, что объект не прилагается. Итак, как мне избавиться от всех добавленных, но несохраненных объектов?

+7

Я бы рекомендовал не повторного использования ObjectContext. Есть сценарии, где это имеет смысл, но они очень редки. Помните, что чем дольше используется ObjectContext, тем больше раздувается он, и если он попадает в несогласованное состояние (то есть что-то частично получается), вы можете получить некоторые непоследовательные побочные эффекты. –

ответ

38

Это была всего лишь тривиальная ошибка, но я собираюсь оставить вопрос здесь - может быть, это помогает другим.

Я имел следующий

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

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

в то время как я хотел следующее

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

и не мог видеть его.

+7

В качестве стороннего варианта можно использовать побитовые флаги для выполнения var objectStateEntries = this._context.ObjectStateManager .GetObjectStateEntries (EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); отделить все прикрепленные объекты –

+0

Отметьте как ответ. –

+0

Обратите внимание, что в некоторых случаях 'objectStateEntry.Entity' будет null (' objectStateEntity' описывает отношение к никакому сущности), поэтому вы, вероятно, должны поместить туда нулевую проверку. –

1

Немного поздно на вечеринку, но вы считали модель работы?

Короче говоря, это позволит вам иметь сделку, которую вы могли бы откатить (отчуждать) или совершить (SaveChanges)

NB: Это сделка, как в группировке изменений в одну операцию, а не как в SQL BEGIN TRAN. Этот метод все еще обрабатывается с помощью метода SaveChanges().

Что-то вроде:

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 

Каждый раз, когда вы создаете единицу работы, она занимает в ObjectContext - Мы используем Unity, чтобы генерировать эти так, что создается новый, если старый был

+2

Использование единицы работы по-прежнему не отделяет объекты от контекста. Он также не отвечает на вопрос. –

+0

@AgentShark Зависит от того, как вы применяете метод 'Rollback'. Кроме того, нет смысла дублировать правильный код в принятом ответе, который явно показывает, как отделять объекты. – Basic

+0

Я перечитал вопрос. Было бы полезно использовать форсирование обновлений через единицу работы. Однако в EF вы должны быть осторожны при добавлении объекта со связанными объектами. Существуют разные способы с различными поведением для добавления/сохранения объектов в контексте. –

36

Ответ Даниэля работал на меня, однако API-интерфейс EntityFramework отличается от версии 6+. Вот метод, который я добавил в мой пользовательский контейнер хранилища, который отключит все объекты из ChangeTracker в 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

этот метод работает для меня :-D спасибо! – duardbr

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

Для тех, кто использует Entity Framework Ядро 1.0 RC2 +, я обновил ответ Garrys.

Ссылка

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

Обновленный код

 /// <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; 
       } 
      } 
     } 
Смежные вопросы