EF 5 Таймаут при обновлении; Возможно, из-за тупика

У меня есть инструмент командной строки для выполнения массового импорта / экспорта записей данных через Entity Framework в базу данных приложений. Этот инструмент хорошо работает для вставки новых записей в базу данных, но при попытке обновить существующие записи, которые, как представляется, связаны с блокировкой EF, я столкнулся с ошибкой таймаута.

Я прочитал некоторые из многих других сообщений о Entity Framework и тупиках, но ни один из ответов, похоже, не применим к этой ситуации. Я попробовал обернуть свой код импорта в TransactionScope а также выполнить команду SQL SET TRANSACTION ISOLATION LEVEL , но ни один из них не задает тайм-аут.

Тайм-аут происходит независимо от того, сколько объектов обновляется в одном вызове SaveChanges . В приведенном ниже примере кода я установил размер партии в значения от 1 до 500, всегда с тем же исключением.

Вот сжатая версия кода обновления, за которой следуют сведения об исключении и скриншот монитора мониторинга SQL Server.

Я использую объекты Entity Framework 5 DbContext, которые инициализируются из моделей EDMX (модель First).

 using(var readContext = new MySourceEntities()) using(var readWriteContext = new MyTargetEntities()) { var query = "SELECT ..."; // Determine which records to update var keys = readContext.Database.SQLQuery<int>(query); // Group the update into batches to improve performance. Batch() // extension method from MoreLINQ foreach (var batch in keys.Batch(BATCH_SIZE)) { var sourceRecords = readContext .AsNoTracking() .Where(x => batch.Contains(x.SharedId)) .ToList(); var targetRecords = readWriteContext .Where(x => batch.Contains(x.SharedId)) .ToLookup(x => x.SharedId); foreach (var record in sourceRecords) { // Enforce a constraint on having only a single match var target = targetRecords[record.SharedId].Single(); target.Field = record.Field; } readWriteContext.SaveChanges(); // <--- Timeout happens here } } 

Я запускаю это из приложения с командной строкой, и конкретная трассировка стека, которую он создает, выглядит следующим образом:

 An error occurred while updating the entries. See the inner exception for details. at System.Data.Entity.Internal.InternalContext.SaveChanges() at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() at System.Data.Entity.DbContext.SaveChanges() at <snip> An error occurred while updating the entries. See the inner exception for details. at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) at System.Data.Entity.Internal.InternalContext.SaveChanges() Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. The statement has been terminated. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues) at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) 

Пока метод SaveChanges висит, в мониторе активности SQL Server отображаются следующие запросы, все в состоянии SUSPENDED . Красные запросы поступают из базы данных, ориентированной на readContext а синие запросы – из базы данных, ориентированной на readWriteContext .

Монитор активности SQL Server Management Studio

Кроме того, приостановленные запросы сами не выглядят подозрительными, просто команда SELECT и UPDATE. Я могу запускать их вручную без ошибок.

редактировать

Ниже приведены сведения о query , которое выполняется, поскольку оно, по-видимому, имеет значение. Запрос выполняет объединение между базами данных для сопоставления записей с SharedId . sys.dm_os_waiting_tasks запрос sys.dm_os_waiting_tasks с этой страницы , вы получите следующую таблицу.

 session_id wait_duration_ms wait_type blocking_session_id resource_description program_name text 55 15 ASYNC_NETWORK_IO NULL NULL EntityFramework <cross-db join query> 54 29310 LCK_M_IX 55 pagelock fileid=1... EntityFramework <update query> 

Содержимое запроса выглядит так:

 SELECT DB1.dbo.Table1.SharedId FROM DB2.dbo.Table2 INNER JOIN DB1.dbo.Table1.SharedId ON DB1.dbo.Table1.SharedId = DB2.dbo.Table2.SharedId WHERE ( (DB1.dbo.Table1.Field1 <> DB2.dbo.Table2.Field1) OR (DB1.dbo.Table1.Field2 <> DB2.dbo.Table2.Field2) ) 

Самое удивительное замечание для меня заключается в том, что запрос все еще активен. Любые идеи, почему readContext.Database.SQLQuery() не завершил запрос? Похоже, этот тип ожидания обычно указывает на ошибку приложения, но я не уверен, как я запускаю это поведение.

Решение заключалось в том, чтобы материализовать результаты SQLQuery() явно с помощью ToList() расширения ToList() . Это позволило полностью расходуть результаты, прежде чем пытаться UPDATE базовые таблицы.

Удивительно (мне), что прямая команда SQL не сразу вернула полный результат – тем более, что запрос возвращал примитивный тип, а не объект Entity. При более близком чтении документации MSDN он указывает, что IEnumerable<T> возвращаемый методом SQLQuery , «выполнит запрос при его перечислении».

Мне нравится, что в EF6 этот метод возвращает объект DbRawSqlQuery<T> , который, вероятно, поможет таким людям, как я, перестать читать и читать документацию в следующий раз.

  • Оптимистический параллелизм: IsConcurrencyToken и RowVersion
  • Создание соединения с MDF-файлом в Visual Studio: «Файл используется»
  • Как сначала развертывать код инфраструктуры сущности для отдельного приложения
  • Entity Framework Подключение к localDb
  • Как мое приложение одновременно может использовать базу данных MS Access и базу данных SQL Server?
  • Как бороться с проблемами параллелизма sql на веб-сайте
  • Первичный ключ, основанный на внешних ключах EF
  • Перехват не работает, как ожидалось, с Entity Framework 6
  • Entity Framework MVC Медленная загрузка страниц
  • Ошибка MSSQL. Исходный провайдер не смог открыть Open '
  • Может ли первая кодовая структура Entity Framework выполнять кросс-запросы базы данных с базами данных SQL Server в одном окне?
  • Interesting Posts

    Какой инструмент для создания простого веб-интерфейса в моей базе данных

    Как изменить текст заголовка для SQL Server Management Studio?

    Как удалить начальный ноль (ы) из SQL

    Как получить следующее число в последовательности

    Правильный способ управления индексами базы данных с помощью Entity Framework

    Перечислите конкретную информацию из нескольких таблиц.

    Столбец SSRS и линейная диаграмма?

    Получить время в США по местному времени

    Назначить базу данных SQL Server вместо статических данных

    Триггер Inserted и Deleted сменного первичного ключа (SQL Server)

    Выбор данных из таблицы при извлечении значения MAX столбца

    Как присоединиться к 4 + таблицам в Access с комбинацией внутренней / внешней?

    возможно ли сохранить метаданные о файле mdf внутри файла?

    Проверка нарушений правил перед удалением записи

    B-деревья, базы данных, последовательные и случайные вставки и скорость. Случайный выигрыш

    Давайте будем гением компьютера.