Вставьте огромное количество строк в базу данных с помощью Entity Framework

@EDIT Я выполнил шаги из Самый быстрый способ вставки в Entity Framework и получил еще худшие результаты, так что это не дубликат.

Моя цель — создать начальный метод для заполнения одной из таблиц LocalDb. Метод добавит 182 500 строк (для имитации годовых данных об активности для 500 устройств) для дальнейшего тестирования. Возможно, я захочу запустить его еще несколько раз, чтобы изменить количество устройств, чтобы было сгенерировано еще больше строк. Вот почему мне нужно вставлять строки как можно эффективнее.

    protected void SeedReportDataTable(int numberOfTerminals)
    {
        var rand = new Random();
        var tidsList = new List<string>();

        // generuj liste losowych numerow tid
        for (int i = 0; i < numberOfTerminals; i++)
        {
            var randomTid = rand.Next(100000, 1000000).ToString(); // generuj 6-cyfrowy numer tid
            while (tidsList.Contains(randomTid)) { randomTid = rand.Next(100000, 1000000).ToString(); } // elminuj powtorzenia
            tidsList.Add(randomTid);
        }

        // dla kazdego z numerow tid generuj roczna historie aktywnosci
        var recordsList = new BlockingCollection<ReportData>();
        int year = Convert.ToInt32(DateTime.Now.Year);

        Parallel.ForEach(tidsList, tid =>
        {
            // dla kazdego miesiaca
            for (int month = 1; month <= 12; month++)
            {
                // dla kazdego dnia
                for (int day = 1; day <= DateTime.DaysInMonth(year, month); day++)
                {
                    var record = new ReportData
                    {
                        Tid = tid,
                        Active = Convert.ToBoolean(
                            rand.Next(0, 11)), // generuj losowy stan aktywnosci z prawdopodbienstwem 1/10 na bycie nieaktywnym
                        Date = new DateTime(year, month, day)
                    };
                    recordsList.Add(record);
                }
            }
        });
        // dodaj unikalne klucze glowne rekordom przed dodaniem do kontekstu bazy
        var keyValue = 1;

        foreach (var record in recordsList)
        {
            record.Id = keyValue++;
        }

        // podziel liste na czesci
        int chunkSize = 1000;

        for (int recordsSkipped = 0; recordsSkipped < recordsList.Count; recordsSkipped += chunkSize)
        {
            // wymieniaj kontekst
            using (var db = new dbEntities())
            {
                db.Configuration.AutoDetectChangesEnabled = false;
                db.Configuration.ValidateOnSaveEnabled = false;
                // dodawaj do bazy po kawalku
                db.ReportData.AddRange(recordsList.Skip(recordsSkipped).Take(chunkSize));
                db.SaveChanges();
            }
        }
    }

Выполнение этого кода заняло 30 минут. До этого я запускал версию, заканчивающуюся на:

using (var db = new dbEntities())
{
    db.ReportData.AddRange(recordsList);
    db.SaveChanges();
}

и это заняло 15 минут, что все же медленнее, чем я ожидал.

Почему мои "улучшения" не увенчались успехом?

Что я могу сделать, чтобы он вставлял строки быстрее?


person adriangutowski    schedule 26.08.2017    source источник
comment
Возможный дубликат Самый быстрый способ вставки в Entity Framework   -  person Reyan Chougle    schedule 26.08.2017
comment
Собираетесь ли вы использовать LocalDB в производстве или Sql Server? Если вы беспокоитесь о производственной производительности, вам следует сравнить с Sql Server, если это то, что будет работать в производственной среде.   -  person Igor    schedule 26.08.2017
comment
Тем не менее, заполнение БД таким количеством записей с использованием EF - не самый эффективный выбор. Вместо этого рассмотрите возможность использования Sql Bulk Insert, если производительность имеет первостепенное значение при вставке большого количества записей (миллионов и более). Даже в этом случае вы также должны проверить Sql Server на наличие узких мест (т. е. обновления индекса и обновления статистики для вставок также могут замедлить такое пакетное задание).   -  person Igor    schedule 26.08.2017
comment
Я использую localdb, чтобы иметь данные для тестирования других моих методов создания отчетов в формате PDF. Этот метод предназначен исключительно для тестирования. Это не дубликат, потому что я следовал советам из этой темы и получил еще худшие результаты.   -  person adriangutowski    schedule 26.08.2017
comment
Если это для тестирования, то почему вас волнует, очень быстро это или нет?   -  person Igor    schedule 26.08.2017
comment
Я уже объяснил. Возможно, я захочу использовать этот метод еще раз, добавив еще больше строк, и я не хочу каждый раз ждать 15-30 минут. Кроме того, я попытался улучшить его с помощью советов из другой темы и сделал его вдвое хуже. Не могли бы вы объяснить мне, почему?   -  person adriangutowski    schedule 26.08.2017
comment
1. Вы создаете свой dbContext 1850 раз. так что это будет медленно, однако 2. Попробуйте увеличить размер блока до 10000 или даже больше, если это не проблема.   -  person sTrenat    schedule 26.08.2017
comment
Попробуйте что-то вроде этого: using (var db = new dbEntities()) { db.Configuration.AutoDetectChangesEnabled = false; db.Configuration.ValidateOnSaveEnabled = ложь; // добавить базу данных для (int recordsSkipped = 0; recordsSkipped ‹ recordsList.Count; recordsSkipped += chunkSize) { db.ReportData.AddRange(recordsList.Skip(recordsSkipped).Take(chunkSize)); БД.СохранитьИзменения(); } }   -  person sTrenat    schedule 26.08.2017
comment
It is not a duplicate because I followed tips from that topic and got even worse results Можете ли вы показать нам версию кода, который вы протестировали с помощью SqlBulkCopy (и рассказать нам, сколько времени потребовалось для его выполнения)?   -  person mjwills    schedule 26.08.2017
comment
@mjwills Вопрос не в том, как быстрее всего вставить записи в базу данных Sql Server из кода .NET. Это все еще дубликат связанного вопроса, но SqlBulkCopy не имеет ничего общего с решением Entity Framework (не зависящим от базы данных). Которого к сожалению нет :(   -  person Ivan Stoev    schedule 26.08.2017
comment
Это правильно @gutowskiap? Для вас важнее использовать Entity Framework? Или важнее, чтобы он был быстрым?   -  person mjwills    schedule 26.08.2017
comment
BlockingCollection не имеет произвольного доступа, насколько мне известно, поэтому повторение recordsList.Skip(...).Take(...) в цикле займет больше времени, чем больше элементов будет пропущено перед взятием. Вам нужно использовать другую структуру данных для Skip/Take или использовать другой подход к фрагментации.   -  person grek40    schedule 26.08.2017
comment
@mjwills Это правда, что я хотел улучшить свой существующий код (используя EF). SqlBulkCopy - интересное решение, но мне пришлось бы преобразовать свой список в datatable. Теперь я тестирую свой код с размером блока 10 000.   -  person adriangutowski    schedule 26.08.2017
comment
I would have to convert my list to datatable. stackoverflow.com/questions/3913371/sqlbulkcopy-from-a-list   -  person mjwills    schedule 26.08.2017


Ответы (1)


Когда я добавляю свой метод заполнения в Configuration.cs и запускаю команду update-database, вставка всех строк занимает менее 5 минут.

Лучше всего работает при вызове Context.AddRange() только один раз.

        dbContext.Configuration.AutoDetectChangesEnabled = false;
        dbContext.Configuration.ValidateOnSaveEnabled = false;
        dbContext.ReportData.AddRange(recordsList);
        dbContext.SaveChanges();
person adriangutowski    schedule 31.08.2017