Я создаю систему пакетной обработки. Партии единиц
поступают в количестве от 20 до 1000 штук. Каждый модуль
по сути представляет собой иерархию моделей (одна основная модель и множество дочерних моделей).Моя задача заключается в сохранении каждой иерархии моделей в базе данных в виде одной транзакции (либо каждая иерархия фиксируется, либо откатывается). К сожалению, EF
не смог обработать две части иерархии модели из-за того, что они могут содержать тысячи записей.
Для решения этой проблемы я настроил SqlBulkCopy
для обработки этих двух моделей с потенциально большим количеством счетчиков и позволил EF
обрабатывать остальные вставки (и ссылочную целостность).
Batch Loop:
foreach (var unitDetails in BatchUnits)
{
var unitOfWork = new Unit(unitDetails);
Task.Factory.StartNew(() =>
{
unitOfWork.ProcessX(); // data preparation
unitOfWork.ProcessY(); // data preparation
unitOfWork.PersistCase();
});
}
Unit:
class Unit
{
public PersistCase()
{
using (var dbContext = new CustomDbContext())
{
// Need an explicit transaction so that
// EF + SqlBulkCopy act as a single block
using (var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions() {
IsolationLevel = System.Transaction.IsolationLevel.ReadCommitted
}))
{
// Let EF Insert most of the records
// Note Insert is all it is doing, no update or delete
dbContext.Units.Add(thisUnit);
dbContext.SaveChanges(); // deadlocks, DbConcurrencyExceptions here
// Copy Auto Inc Generated Id (set by EF) to DataTables
// for referential integrity of SqlBulkCopy inserts
CopyGeneratedId(thisUnit.AutoIncrementedId, dataTables);
// Execute SqlBulkCopy for potentially numerous model #1
SqlBulkCopy bulkCopy1 = new SqlBulkCopy(...);
...
bulkCopy1.WriteToServer(dataTables["#1"]);
// Execute SqlBulkCopy for potentially number model #2
SqlBulkCopy bulkCopy2 = new SqlBulkCopy(...);
...
bulkCopy2.WriteToServer(dataTables["#2"]);
// Commit transaction
scope.Complete();
}
}
}
}
Прямо сейчас я застрял между камнем и наковальней. Если я оставлю для IsolationLevel
значение ReadCommitted
, я получу тупиковые ситуации между операторами EF
INSERT
в различных задачах
.
Если я устанавливаю IsolationLevel
на ReadUncommitted
(что, как я думал, будет нормально, поскольку я не выполняю никаких SELECT
]), я получаю DbConcurrencyExceptions
.
Мне не удалось найти никакой достоверной информации о DbConcurrencyExceptions
и Entity Framework
, но я предполагаю, что ReadUncommitted
по существу вызывает EF
для получения недопустимой информации о «вставленных строках».
ОБНОВЛЕНИЕ
Вот некоторая справочная информация о том, что на самом деле вызывает мои проблемы с взаимоблокировкой при выполнении INSERTS:
По-видимому, эта же проблема присутствовала несколько лет назад, когда Linq Для SQL вышел, и Microsoft исправила его, изменив способ выбора scope_identity (). Не уверен, почему их позиция изменилась на проблему с SQL Server, когда та же проблема возникла с Entity Framework.