Как очистить объект объекта 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 });
Это снова добавит документ A в контекст объекта и, следовательно, SaveChanges()
выдаст исключение из-за дублирующего первичного ключа.
Поэтому я должен удалить все уже добавленные документы в случае ошибки проверки. Я мог бы, конечно, выполнить проверку сначала и только добавить все документы после того, как они были успешно проверены, но, к сожалению, это не решает всей проблемы - если SaveChanges()
завершается сбой, все документы остаются добавленными, но несохраненными.
Я попытался отсоединить все объекты, возвращенные
this.objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
но я получаю исключение, заявляя, что объект не подключен. Итак, как мне избавиться от всех добавленных, но несохраненных объектов?
Ответы
Ответ 1
Это была всего лишь тривиальная ошибка, но я собираюсь оставить вопрос здесь - возможно, это помогает другим.
У меня было следующее
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);
}
и не мог его увидеть.
Ответ 2
Ответ Даниэля сработал для меня, однако API-интерфейс EntityFramework отличается в версии 6+. Вот метод, который я добавил в свой пользовательский контейнер репозитория, который отсоединит все сущности от DbContext ChangeTracker:
/// <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;
}
}
}
Ответ 3
Немного поздно для вечеринки, но вы считали шаблон Unit of Work?
Короче говоря, это позволит вам иметь транзакцию, которую вы могли бы откат (распоряжаться) или совершить (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
Каждый раз, когда вы создаете единицу работы, он принимает объект contexttext. Мы используем Unity для их создания, чтобы новый был создан, если старый был удален
Ответ 4
var entries = ((DbContext)ef).ChangeTracker.Entries();
foreach (var entry in entries)
{
entry.State = System.Data.EntityState.Detached;
}
Ответ 5
Для тех, кто использует Entity Framework Core 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;
}
}
}