Конкурирующие вызовы EF SaveChanges ()

Я создаю систему пакетной обработки. Партии единиц поступают в количестве от 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:

http://connect.microsoft.com / VisualStudio / feedback / details / 562148 / как-избежать-использования-scope-identity-based-insert-commands-on-sql-server-2005

По-видимому, эта же проблема присутствовала несколько лет назад, когда Linq Для SQL вышел, и Microsoft исправила его, изменив способ выбора scope_identity (). Не уверен, почему их позиция изменилась на проблему с SQL Server, когда та же проблема возникла с Entity Framework.

5
задан Cœur 1 August 2017 в 17:54
поделиться