Как игнорировать исключение DbUpdateConcurrencyException при удалении объекта
У меня есть приложение, которое считывает много данных в память и обрабатывает их в пакетах.
Я хочу, чтобы инфраструктура entity игнорировала DbUpdateConcurrencyException
при удалении сущности, которая уже была удалена.
Причина в том, что к тому моменту, когда объект был обработан и помечен для удаления, он, возможно, уже был удален из БД.
Неверное удаление строки, которая уже была удалена, не является проблемой и не должна вызывать ошибку, мне просто нужен способ определить структуру сущности, которая:
Пример
Db.Entry(itemToRemove).State = EntityState.Deleted;
Db.SaveChanges();
Вызывает ошибку, если itemToRemove
уже удален.
Примечание: Db.Configuration.ValidateOnSaveEnabled = false;
не исправляет это, как предлагается другой поток.
Ответы
Ответ 1
Как насчет?
Db.Entry(itemToRemove).State = EntityState.Deleted;
bool saveFailed;
do
{
saveFailed = false;
try
{
Db.SaveChanges();
}
catch(DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
//The MSDN examples use Single so I think there will be only one
//but if you prefer - do it for all entries
//foreach(var entry in ex.Entries)
//{
if(entry.State == EntityState.Deleted)
//When EF deletes an item its state is set to Detached
//http://msdn.microsoft.com/en-us/data/jj592676.aspx
entry.State = EntityState.Detached;
else
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
//throw; //You may prefer not to resolve when updating
//}
}
} while (saveFailed);
Подробнее здесь:
Устранение оптимистических concurrency исключений
Ответ 2
Вы можете обрабатывать DbUpdateConcurrencyException
, а затем вызывать Refresh(RefreshMode,IEnumerable)
с помощью RefreshMode.StoreWins и ваших удаленных объектов в качестве параметра.
try{
Db.Entry(itemToRemove).State = EntityState.Deleted;
Db.SaveChanges();
}
catch(DbUpdateConcurrencyException)
{
IObjectContextAdapter adapter = Db;
adapter.ObjectContext.Refresh(RefreshMode.StoreWins, context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Deleted));
Db.SaveChanges();
}
Ответ 3
На основе кода https://msdn.microsoft.com/en-US/data/jj592904, но где я добавил счетчик бесконечных циклов (на всякий случай, вы никогда не знаете, правильно?) и перебирает все записи в списке исключений.
var maxTriesCounter = 20;
bool saveFailed;
do
{
saveFailed = false;
maxTriesCounter--;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
foreach (var entry in ex.Entries)
{
entry.Reload();
}
}
} while (saveFailed && maxTriesCounter > 0);
Ответ 4
Вот что я использую. Отсоедините все записи проблем после сохранения.
Db.Entry(itemToRemove).State = EntityState.Deleted;
while(true)
try {
Db.SaveChanges();
break;
} catch (DbUpdateConcurrencyException ex) {
ex.Entries.ToList().ForEach(x=>x.State=EntityState.Detached);
}
Или вы можете добавить пользовательскую функцию SaveChanges в свой класс DbContext и использовать ее вместо этого, когда вам нужно проигнорировать эти ошибки.
public int SaveChanges_IgnoreConcurrencyExceptions () {
while(true)
try {
return this.SaveChanges();
} catch (DbUpdateConcurrencyException ex) {
ex.Entries.ToList().ForEach(x => x.State=EntityState.Detached);
}
}