Я строю систему пакетной обработки.Партии Units
поступают в количестве от 20 до 1000.Каждый Unit
представляет собой иерархию моделей (одна основная модель и несколько дочерних моделей).Моя задача заключается в сохранении каждой иерархии модели в базе данных в виде одной транзакции (либо каждая иерархия фиксируется, либо откатывается).К сожалению, EF
не смог обработать две части иерархии моделей из-за их способности содержать тысячи записей.
То, что я сделал для решения этой проблемы, настроено SqlBulkCopy
для обработки этих двух моделей с потенциально высоким числом и для EF
обработки остальных вставок (и ссылочной целостности).
Пакетная петля:
foreach (var unitDetails in BatchUnits)
{
var unitOfWork = new Unit(unitDetails);
Task.Factory.StartNew(() =>
{
unitOfWork.ProcessX(); // data preparation
unitOfWork.ProcessY(); // data preparation
unitOfWork.PersistCase();
});
}
Единица измерения:
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
операторами в разных Tasks
.
Если я установлю IsolationLevel
на ReadUncommitted
(что, я думаю, будет в порядке, поскольку я не делаю SELECTs
), я получу DbConcurrencyExceptions
.
У меня естьНе удалось найти какую-либо полезную информацию о DbConcurrencyExceptions
и Entity Framework
, но я предполагаю, что ReadUncommitted
по существу заставляет EF
получать недопустимую информацию о вставленных строках.
UPDATE
Вот некоторая справочная информация о том, что на самом деле вызывает мои проблемы с блокировкой при выполнении INSERTS:
http://connect.microsoft.com/VisualStudio/feedback/details/562148/how-to-avoid-using-scope-identity-based-insert-commands-on-sql-server-2005
Видимо, эта же проблема присутствовала несколько лет назадкогда вышел Linq To SQL и Microsoft исправила это, изменив способ выбора scope_identity ().Не уверен, почему их позиция изменилась на проблему SQL Server, когда та же проблема возникла с Entity Framework.