Оптимизация производительности ядра Entity Framework для размещения очень большой папки одной и той же таблицы

#c# #xml #entity-framework-core #etl #bulkinsert

#c# #xml #entity-framework-core #etl #bulkinsert

Вопрос:

У меня есть фоновая служба, которая на C # загружает 3600 XML-файлов (общий размер файла 5 ГБ) в базу данных SQL Server. Продолжительность завершения приема составляет около 16 часов. Я использую hangfire для создания 3 заданий / потоков, и у каждого задания будет одна папка для загрузки, папка A, B, C.

Проблема в том, что папка C очень тяжелая. Моя идея состоит в том, чтобы разделить файлы в папке C на две папки, папку C1 и папку C2. Итак, теперь у меня есть 4 задания / потока, папки A, B, C1 и C2. Но проблема в том, что задание C1 и C2 попали в ошибку базы данных, ниже которой, я полагаю, из-за того, что они оба оценивают одну и ту же таблицу.

При сохранении изменений для контекста типа ‘xxxContext’ в базе данных возникло исключение. Система.Исключение InvalidOperationException: вторая операция, запущенная в этом контексте до завершения предыдущей операции. Обычно это вызвано тем, что разные потоки используют один и тот же экземпляр DbContext

и в другой раз с этой ошибкой:

При сохранении изменений для контекста типа ‘xxxContext’ в базе данных возникло исключение. Система.Исключение InvalidOperationException: коллекция была изменена; операция перечисления может не выполняться.

и ошибка от hangfire ниже:

Hangfire.Хранение.Истек тайм-аут DistributedLockTimeoutException. Время ожидания истекло до получения распределенной блокировки для ‘HangFire: IIngestService.Ресурс IngestPersonXML.

Hangfire.Хранение.Исключение DistributedLockTimeoutException: истек тайм-аут. Время ожидания истекло до получения распределенной блокировки для ‘HangFire: IIngestService.Ресурс IngestPersonXML.

Когда я использую Parallel.ForEach , я также получаю эту ошибку:

Система.Исключение InvalidOperationException: «Операции, которые изменяют не параллельные коллекции, должны иметь эксклюзивный доступ. Одновременное обновление было выполнено для этой коллекции и повредило ее состояние. Состояние коллекции больше не является правильным. ‘

Мне нужно только вставить в БД. Не требуется операция обновления или удаления. Есть ли какое-либо обходное решение для этого?

Комментарии:

1. Каждый поток должен иметь свой собственный DbContext, который также воссоздает DbContext для каждого файла. Конечно, для скорости вам нужно использовать сторонние расширения, например github.com/linq2db/linq2db.EntityFrameworkCore context.BulkCopy(aLotOfitems)

2. @SvyatoslavDanyliv зачем тогда вообще проходить через EF? Ничего не получается при использовании ORM. Все, что нужно SqlBulkCopy, — это IDataReader, то, что можно создать из любой коллекции. Копирование объектов, десериализованных из XML, в DbContext ничего не дает, только для того, чтобы снова извлечь их и отправить в SqlBulkCopy. Почему бы не избежать повторяющихся объектов и накладных расходов?

3. @PanagiotisKanavos, я не знаю, это современно, популярно, хорошо документировано 😉 Я никогда не использовал EF из-за нашей библиотеки и никогда не иду на компромиссы. Запросы и все остальное должно быть как можно быстрее.

4. @Steve как вы десериализуете XML-файлы? Насколько сложна эта схема? На какую базу данных вы ориентируетесь? EF Core и любой ORM в этом случае непригодны, поэтому ни одна из рассмотренных вами стратегий не поможет. В большинстве баз данных есть способ импортировать XML-данные. Многие базы данных имеют способы удаленного массового импорта данных — поставщики SQL Server, MySQL и PostgreSQL .NET уже предлагают эту функциональность

5.@Steve SQL Server уже может импортировать XML-файлы, OPENROWSET как показано здесь. Это, вероятно, самый простой и, вероятно, самый быстрый вариант. Если объекты уже находятся в памяти, вы можете использовать ObjectReader FastMember для создания оболочки IDataReader, которую вы можете напрямую передать в SqlBulkCopy.

Ответ №1:

EF не предназначен для такого рода операций. Для этого используйте SqlBulCopy.
Существуют библиотеки, которые легко предоставляют его для EF, но вы также можете написать свою собственную реализацию — это не так сложно

Действительно не понимаю этой части

Мне нужно только вставить в БД. Требуется выполнить операцию обновления или удаления. Есть ли какое-либо обходное решение для этого?

Так вам нужно обновление или нет? Ну .. если вам нужно обновить кучу строк, вставьте их с помощью массового копирования во временную таблицу, а затем просто выполните обновление соединения.

Комментарии:

1. Извините. Опечатка. Это должно быть Do update or delete operation needed