Я ищу самый быстрый способ вставки в Entity Framework.
Я спрашиваю об этом из-за сценария, где у вас есть активный TransactionScope, и вставка огромна (4000+). Потенциально он может длиться более 10 минут (время ожидания транзакций по умолчанию), и это приведет к незавершенной транзакции.
c#
sql
entity-framework
Бонго Шарп
источник
источник
Ответы:
К вашему замечанию в комментариях к вашему вопросу:
Это худшее, что вы можете сделать! Вызов
SaveChanges()
для каждой записи значительно замедляет массовые вставки. Я бы сделал несколько простых тестов, которые, скорее всего, улучшат производительность:SaveChanges()
один раз после ВСЕХ записей.SaveChanges()
после, например, 100 записей.SaveChanges()
например, 100 записей, удалите контекст и создайте новый.Для массовых вставок я работаю и экспериментирую с таким шаблоном:
У меня есть тестовая программа, которая вставляет 560 000 объектов (9 скалярных свойств, без свойств навигации) в БД. С этим кодом он работает менее чем за 3 минуты.
Для исполнения важно позвонить
SaveChanges()
после «много» записей («много» около 100 или 1000). Это также повышает производительность для удаления контекста после SaveChanges и создания нового. Это очищает контекст от всех объектов,SaveChanges
не делает этого, объекты все еще привязаны к контексту в состоянииUnchanged
. Это растущий размер присоединяемых объектов в контексте, который замедляет вставку шаг за шагом. Таким образом, полезно очистить его через некоторое время.Вот несколько измерений для моих 560000 объектов:
Поведение в первом тесте, приведенном выше, заключается в том, что производительность очень нелинейная и сильно снижается со временем. («Много часов» - оценка, я никогда не заканчивал этот тест, я остановился на 50 000 сущностей через 20 минут.) Это нелинейное поведение не столь существенно во всех других тестах.
источник
AutoDetectChangesEnabled = false;
на DbContext. Он также имеет большой дополнительный эффект производительности: stackoverflow.com/questions/5943394/…DbContext
, НЕТObjectContext
?Эта комбинация достаточно хорошо увеличивает скорость.
источник
Самый быстрый способ будет использовать расширение массовой вставки , которое я разработал
примечание: это коммерческий продукт, не бесплатный
Он использует SqlBulkCopy и пользовательские устройства чтения данных, чтобы получить максимальную производительность. В результате это более чем в 20 раз быстрее, чем при использовании обычной вставки или AddRange.
использование очень просто
источник
Вы должны посмотреть на использование
System.Data.SqlClient.SqlBulkCopy
для этого. Вот документация , и, конечно, есть много учебных пособий онлайн.Извините, я знаю, что вы искали простой ответ, чтобы заставить EF делать то, что вы хотите, но массовые операции не совсем то, для чего предназначены ORM.
источник
Я согласен с Адамом Рэкисом.
SqlBulkCopy
это самый быстрый способ передачи массовых записей из одного источника данных в другой. Я использовал это для копирования 20K записей, и это заняло менее 3 секунд. Посмотрите на пример ниже.источник
AsDataReader()
этом ответе описан метод расширения: stackoverflow.com/a/36817205/1507899Я бы порекомендовал эту статью о том, как делать массовые вставки с использованием EF.
Entity Framework и медленные массовые вставки
Он исследует эти области и сравнивает производительность:
источник
как это никогда не упоминалось здесь, я хочу рекомендовать EFCore.BulkExtensions здесь
источник
Я изучил ответ Слаумы (это потрясающе, спасибо за идею), и я уменьшал размер партии, пока не достиг оптимальной скорости. Глядя на результаты Slauma:
Видно, что скорость увеличивается при перемещении с 1 до 10 и с 10 до 100, но скорость вставки от 100 до 1000 снова падает.
Итак, я сосредоточился на том, что происходит, когда вы уменьшаете размер пакета до значения где-то между 10 и 100, и вот мои результаты (я использую другое содержимое строки, поэтому мое время имеет другое значение):
Исходя из моих результатов, фактический оптимум составляет около 30 для размера партии. Это меньше, чем 10 и 100. Проблема в том, что я понятия не имею, почему 30 оптимален, и я не смог найти логического объяснения этому.
источник
Как говорили другие люди, SqlBulkCopy - это способ сделать это, если вы хотите действительно хорошую производительность вставки.
Это немного громоздко для реализации, но есть библиотеки, которые могут вам в этом помочь. Есть несколько, но на этот раз я постыдно подключу свою собственную библиотеку: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities
Единственный код, который вам понадобится:
Так насколько это быстрее? Трудно сказать, потому что это зависит от множества факторов, производительности компьютера, сети, размера объекта и т. Д. И т. Д. Проведенные мною тесты производительности показывают, что 25 тыс. Объектов можно вставить примерно в 10 секунд стандартным способом. на локальном хосте, если вы оптимизируете свою конфигурацию EF, например упоминается в других ответах. С EFUtilities это занимает около 300 мс. Еще интереснее то, что я сэкономил около 3 миллионов объектов менее чем за 15 секунд, используя этот метод, в среднем около 200 000 объектов в секунду.
Единственная проблема, конечно, если вам нужно вставить опубликованные данные. Это может быть эффективно выполнено на сервере sql с использованием описанного выше метода, но для этого требуется, чтобы у вас была стратегия генерации идентификаторов, которая позволяла бы генерировать идентификаторы в коде приложения для родительского элемента, чтобы вы могли устанавливать внешние ключи. Это можно сделать с помощью идентификаторов GUID или чего-то вроде создания идентификатора HiLo.
источник
EFBatchOperation
имел конструктор, который вы передаете в,DbContext
а не в каждый статический метод. Общие версииInsertAll
иUpdateAll
которые автоматически находят коллекцию, похожеDbContext.Set<T>
, тоже подойдут.Dispose()
контекст создает проблемы, если сущности, на которые выAdd()
полагаетесь в других предварительно загруженных сущностях (например, свойства навигации) в контекстеЯ использую аналогичную концепцию, чтобы сохранить мой контекст небольшим, чтобы достичь той же производительности
Но вместо
Dispose()
контекста и воссоздания я просто отсоединяю сущности, которые ужеSaveChanges()
оберните его
TrasactionScope()
командой try catch и, если вам нужно, не показывайте их здесь для поддержания чистоты кодаисточник
Я знаю, что это очень старый вопрос, но один парень сказал, что разработал метод расширения для использования массовой вставки с EF, и когда я проверил, я обнаружил, что библиотека сегодня стоит 599 долларов (для одного разработчика). Может быть, это имеет смысл для всей библиотеки, однако только для массовой вставки это слишком много.
Вот очень простой метод расширения, который я сделал. Я использую это сначала в паре с базой данных (сначала не проверял код, но я думаю, что это работает так же). Изменить
YourEntities
с именем вашего контекста:Вы можете использовать это против любой коллекции, которая наследуется
IEnumerable
, например:источник
await bulkCopy.WriteToServerAsync(table);
Попробуйте использовать хранимую процедуру , которая получит XML-данные, которые вы хотите вставить.
источник
Я сделал общее расширение примера @Slauma выше;
Применение:
источник
Доступны некоторые сторонние библиотеки, поддерживающие Bulk Insert:
Смотрите: Entity Framework Библиотека массовой вставки
Будьте внимательны при выборе библиотеки массовой вставки. Только Entity Framework Extensions поддерживает все виды ассоциаций и наследований, и пока поддерживается только одна.
Отказ от ответственности : я владелец Entity Framework Extensions
Эта библиотека позволяет вам выполнять все массовые операции, необходимые для ваших сценариев:
пример
источник
Используйте
SqlBulkCopy
:источник
Один из самых быстрых способов сохранить список, вы должны применить следующий код
AutoDetectChangesEnabled = false
Add, AddRange & SaveChanges: не обнаруживает изменений.
ValidateOnSaveEnabled = false;
Не обнаруживает изменения трекер
Вы должны добавить нюгет
Теперь вы можете использовать следующий код
источник
SqlBulkCopy супер быстрый
Это моя реализация:
источник
[2019 Обновление] EF Core 3.1
Следуя тому, что было сказано выше, отключение AutoDetectChangesEnabled в EF Core работало отлично: время вставки было разделено на 100 (от нескольких минут до нескольких секунд, 10 тыс. Записей с взаимосвязями между таблицами)
Обновленный код:
источник
Вот реалистичное сравнение производительности между использованием Entity Framework и классом SqlBulkCopy: Как массово вставить сложные объекты в базу данных SQL Server
Как уже подчеркивали другие, ORM не предназначены для массовых операций. Они предлагают гибкость, разделение проблем и другие преимущества, но массовые операции (кроме массового чтения) не являются одними из них.
источник
Другой вариант - использовать SqlBulkTools, доступный от Nuget. Он очень прост в использовании и обладает рядом мощных функций.
Пример:
См. Документацию для большего количества примеров и продвинутого использования. Отказ от ответственности: я являюсь автором этой библиотеки, и любые взгляды имеют собственное мнение.
источник
По моим сведениям есть
no BulkInsert
вEntityFramework
увеличении производительности огромных вставок.В этом случае вы можете пойти с SqlBulkCopy в ,
ADO.net
чтобы решить вашу проблемуисточник
WriteToServer
что занимаетDataTable
.Вы когда-нибудь пытались вставить через фоновый рабочий или задачу?
В моем случае я вставляю 7760 регистров, распределенных в 182 разных таблицах с отношениями внешних ключей (по NavigationProperties).
Без задачи это заняло 2 с половиной минуты. Внутри задачи (
Task.Factory.StartNew(...)
) это заняло 15 секунд.Я делаю только
SaveChanges()
после добавления всех объектов в контекст. (для обеспечения целостности данных)источник
Все написанные здесь решения не помогают, потому что когда вы выполняете SaveChanges (), операторы вставки отправляются в базу данных один за другим, именно так работает Entity.
И если ваша поездка в базу данных и обратно составляет, например, 50 мс, то время, необходимое для вставки, равно числу записей x 50 мс.
Вы должны использовать BulkInsert, вот ссылка: https://efbulkinsert.codeplex.com/
Я использовал время вставки с 5-6 минут до 10-12 секунд, используя его.
источник
Вы можете использовать пакет Bulk библиотеку. Bulk Insert 1.0.0 version используется в проектах, имеющих Entity Framework> = 6.0.0.
Более подробное описание можно найти здесь - исходный код Bulkoperation
источник
[НОВОЕ РЕШЕНИЕ ДЛЯ POSTGRESQL] Эй, я знаю, что это довольно старый пост, но недавно я столкнулся с подобной проблемой, но мы использовали Postgresql. Я хотел использовать эффективный булькинсерт, что оказалось довольно сложно. Я не нашел подходящей бесплатной библиотеки для этой базы данных. Я нашел только этого помощника: https://bytefish.de/blog/postgresql_bulk_insert/, который также находится на Nuget. Я написал небольшой преобразователь, который автоматически сопоставляет свойства способом Entity Framework:
Я использую его следующим образом (у меня была сущность с именем Undertaking):
Я показал пример с транзакцией, но это также можно сделать с обычным соединением, полученным из контекста. takekingsToAdd - это перечисление обычных записей сущностей, которые я хочу добавить в БД.
Это решение, к которому я пришел после нескольких часов исследований и попыток, - это то, что вы могли ожидать гораздо быстрее и, наконец, простое в использовании и бесплатное! Я действительно советую вам использовать это решение не только по причинам, упомянутым выше, но и потому, что оно единственное, с которым у меня не было проблем с самим Postgresql, многие другие решения работают безупречно, например с SqlServer.
источник
Секрет в том, чтобы вставить в идентичный пустой промежуточный стол. Вставки быстро осветляются. Затем запустить один вставку из этого в ваш главный большой стол. Затем обрежьте промежуточный стол, готовый к следующей партии.
то есть.
источник
Но для более чем (+4000) вставок я рекомендую использовать хранимую процедуру. приложил время, прошедшее. Я вставил 11.788 строк в 20 "
вот это код
источник
Используйте хранимую процедуру, которая принимает входные данные в форме XML для вставки данных.
Из вашего кода на c # вставьте данные в формате xml.
Например, в C # синтаксис будет выглядеть так:
источник
Используйте эту технику, чтобы увеличить скорость вставки записей в Entity Framework. Здесь я использую простую хранимую процедуру для вставки записей. И для выполнения этой хранимой процедуры я использую метод .FromSql () Entity Framework, который выполняет Raw SQL.
Код хранимой процедуры:
Затем переберите все ваши 4000 записей и добавьте код Entity Framework, который выполняет сохраненные
Процедура начинается каждый сотый цикл.
Для этого я создаю строковый запрос для выполнения этой процедуры, продолжая добавлять к нему каждый набор записей.
Затем проверьте, что цикл работает кратно 100, и в этом случае выполните его, используя
.FromSql()
.Проверьте код ниже:
источник