Подтвердить что ты не робот

Как ускорить DbSet.Add()?

Мне нужно импортировать около 30 тыс. строк из файла CSV в мою базу данных SQL, это, к сожалению, занимает 20 минут.

Устранение неполадок с профилировщиком показывает, что DbSet.Add занимает больше всего времени, но почему?

У меня есть классы Entity Framework Code-First:

public class Article
{
    // About 20 properties, each property doesn't store excessive amounts of data
}

public class Database : DbContext
{
    public DbSet<Article> Articles { get; set; }
}

Для каждого элемента в моем цикле for я делаю:

db.Articles.Add(article);

За пределами цикла for я делаю:

db.SaveChanges();

Он связан с моим локальным сервером SQLExpress, но я думаю, что ничего не написано до тех пор, пока SaveChanges не будет вызван, поэтому я думаю, что сервер не будет проблемой....

4b9b3361

Ответ 1

Каждый элемент в единице работы имеет накладные расходы, так как он должен проверять (и обновлять) диспетчер идентификации, добавлять в различные коллекции и т.д.

Первое, что я попробую, это доработать, скажем, группы из 500 (измените это число), начиная со свежего (нового) объекта-контекста каждый раз, так как в противном случае вы можете разумно ожидать телескопической производительности. Разбивание его на партии также мешает мегалитической сделке, приносящей все к остановке.

Помимо этого; SqlBulkCopy. Он предназначен для большого импорта с минимальными накладными расходами. Это не EF, хотя.

Ответ 2

По словам Кевина Рамена (29 марта) Я могу подтвердить, что установка db.Configuration.AutoDetectChangesEnabled = false делает огромную разницу в скорости

Запуск Add() по 2324 элементам по умолчанию выполнялся 3мин 15 секунд на моей машине, отключив автообнаружение, завершив операцию в 0.5 сек.

http://blog.larud.net/archive/2011/07/12/bulk-load-items-to-a-ef-4-1-code-first-aspx

Ответ 3

Я добавлю к замечанию Кервина Рамена, сказав, что если вы делаете только вставки (никаких обновлений или удалений), вы можете, в общем, безопасно установить следующие свойства перед выполнением любых вставок в контексте:

DbContext.Configuration.AutoDetectChangesEnabled = false;
DbContext.Configuration.ValidateOnSaveEnabled = false;

У меня возникла проблема с однократным массовым импортом на моей работе. Не устанавливая вышеуказанные свойства, добавление около 7500 сложных объектов в контекст занимало более 30 минут. Установка вышеуказанных свойств (так что отключение проверки EF и отслеживание изменений) уменьшало импорт до секунд.

Но, опять же, я подчеркиваю, что использую это только в том случае, если вы делаете вставки. Если вам нужно смешать вставки с обновлениями/удалениями, вы можете разделить свой код на два пути и отключить проверки EF для части вставки, а затем снова включить проверки пути обновления/удаления. Я использовал этот подход, чтобы обойти медленное поведение DbSet.Add().

Ответ 4

Здесь очень простое и быстрое расширение: https://efbulkinsert.codeplex.com/

Он называется "Массивная вставка Entity Framework".

Внутреннее расширение находится в пространстве имен EntityFramework.BulkInsert.Extensions. Поэтому, чтобы выявить метод расширения, добавьте

using EntityFramework.BulkInsert.Extensions;

И тогда вы можете сделать это

context.BulkInsert(entities);

BTW. Если вы не хотите использовать это расширение по какой-либо причине, вы также можете попробовать вместо запуска db.Articles.Add(статьи) для каждой статьи, чтобы каждый раз создавать список из нескольких статей, а затем использовать AddRange (новый в EF версии 6 вместе с RemoveRange), чтобы добавить их вместе в dbcontext.

Ответ 5

Я действительно не пробовал это, но моя логика заключалась бы в том, чтобы поддерживать драйвер ODBC для загрузки файла в datatable, а затем использовать хранимую процедуру sql для передачи таблицы в процедуру.

Для первой части попробуйте: http://www.c-sharpcorner.com/UploadFile/mahesh/AccessTextDb12052005071306AM/AccessTextDb.aspx

Для второй части попробуйте это для процедуры SQL: http://www.builderau.com.au/program/sqlserver/soa/Passing-table-valued-parameters-in-SQL-Server-2008/0,339028455,339282577,00.htm

И создайте объект SqlCommnand в С# и добавьте в его набор параметров SqlParameter, который является SqlDbType.Structured

Ну, надеюсь, это поможет.