Как повысить производительность сохранения данных в Entity Framwork с помощью функции db.savechange () в рекурсивной функции C # или другой доступной опции? - PullRequest
3 голосов
/ 27 марта 2019

Я сохраняю и обновляю записи с помощью рекурсивной функции, но она использует слишком много времени - около 2 минут для сохранения записей.Я использую Entity Framework для доступа к базе данных.

Рекомендуется наилучшая практика для Entity Framework

Существует N уровня дочерних элементов, поэтому мы не знаем, сколько у нас детей, поэтому я попытался вызвать db.savechange() после завершения рекурсивного анализа.но не повезло.

У меня есть этот скелет кода типа (он не оригинален, я только что добавил структуру).

public void parentFunction(List<DataListforupdate> dataListforupdates, string defaultvalue, Guid perentguid)
{
    if (dataListforupdates != null && dataListforupdates.Count() >)
    {
        foreach (var model in dataListforupdates)
        {
            // manipulate data for save
            saveDateInDB(model.value, model.value, model.value, model.value);

            if (model.child.count() > 0)
            {
                ChildFunction(model.child.dataListforupdates, defaultvalue, perentguid)
            }
        }
    }
    else
    {
        var getAllDataForupdate = db.tbl.where(x => x.gID = perentguid).toList();

        foreach (var model in getAllDataForupdate)
        {
            // manipulate data for save
            saveDateInDB(defaultvalue, model.value, model.value, model.value);

            if (model.child.count() > 0)
            {
                ChildFunction(model.child.dataListforupdates, defaultvalue, perentguid)
            }
        }
    }
}

public void ChildFunction(List<DataListforupdate> dataListforupdates, string defaultvalue, Guid perentguid)
{
    // update model date if its not null
    if (dataListforupdates != null && dataListforupdates.Count() >)
    {
        foreach (var model in dataListforupdates)
        {
            // manipulate data for save
            saveDateInDB(model.value, model.value, model.value, model.value);

            if (model.child.count() > 0)
            {
                ChildFunction(model.child.dataListforupdates, defaultvalue, perentguid)
            }
        }
    }
    // if model date is null then update all record with default value 
    else
    {
        var getAllDataForupdate = db.tbl.where(x => x.gID = perentguid).toList();

        foreach (var model in getAllDataForupdate)
        {
            // manipulate data for save
            saveDateInDB(defaultvalue, model.value, model.value, model.value);

            if (model.child.count() > 0)
            {
                ChildFunction(model.child.dataListforupdates, defaultvalue, perentguid)
            }
        }
    }
}

public void saveDateInDB(string value1, string value2, string value3, string value4)
{
    var getAddedData = db.DataFromTable.where(x => x.id = value1).FirstOrDefault()

    if (getAddedData != null)
    {
        // update date using db.savechanges()
    }
    else
    {
        // Add date using db.savechanges()
    }
}

это проблема скорости, потому что я вызываю db дляполучить список в каждой рекурсии?

Ответы [ 2 ]

2 голосов
/ 27 марта 2019

Я попытался вызвать db.savechange () после завершения рекурсии, но не повезло.

SavingChanges () сразу предполагает улучшение производительности приложения, а не сохранение изменений для каждого дочернего элемента. Это не имеет смысла. На рекурсивном вы должны только изменить состояние объекта на измененное. Также вы можете отключить отслеживание изменений.

Configuration.AutoDetectChangesEnabled = false;
context.Entry(entity).State = EntityState.Modified;

Кроме того, вы должны получать все данные из базы данных одновременно. На рекурсивном не стоит идти в базу данных. Это не лучшая практика.

1 голос
/ 27 марта 2019

Ваш код в настоящее время страдает от 3 проблем исполнения:

  • Обнаружение изменений, вызванных множественным добавлением / присоединением
  • База данных в оба конца, чтобы проверить, существуют ли данные
  • База данных туда и обратно для сохранения данных

Определить изменения, вызванные несколькими добавлениями / прикреплениями

Чем больше сущностей у вас в вашем контексте, тем медленнее становится метод обнаружения изменений: https://entityframework.net/why-detect-changes-slow

Например, при 10000 объектов может потребоваться больше 20 мсек, чтобы добавить одну сущность в трекер изменений, когда первоначально она принимала 0 мс.

Возможное решение:

  • Создайте List<AddedData>, который вы передадите в своем рекурсивном методе. Как только ваша родительская функция завершила добавление всех рекурсивных сущностей в список. Используйте AddRange, чтобы добавить их все в вашем контексте (AddRange выполняет DetectChanges только один раз)
  • Временно отключить обнаружение изменений Configuration.AutoDetectChangesEnabled = false; и повторно включить / выполнить его вручную в родительской функции после добавления всех рекурсивных сущностей.

База данных в оба конца, чтобы проверить, существуют ли данные

Вы совершаете обход базы данных для каждой сущности, чтобы проверить, существует ли она в базе данных.

Таким образом, если вы хотите сохранить 10000 объектов, выполняется обход базы данных в 10 000, и вам нужно найти способ уменьшить его.

Возможное решение:

  • Вместо этого вы можете загрузить все идентификаторы в словарь (только идентификатор, а не всю сущность). это решение прекрасно работает, но становится менее привлекательным по мере роста вашего стола. Так что это не рекомендуется для очень большого стола.
  • Если вы успешно создадите список, а не сохраните его позже, вы можете проверить, используя вместо этого select id + содержит, чтобы проверять сразу несколько объектов, это позволит сократить количество обращений в базу данных (их можно разделить на несколько пакетов). требуется, если у вас слишком много сущностей для сохранения)
  • Если вы успешно создадите список, а не сохраните его позже, используйте BulkMerge из EF Extensions (позже мы поговорим об этом решении)

База данных туда и обратно для сохранения данных

Вы делаете обход базы данных для каждой сущности для сохранения.

Таким образом, если вы хотите сохранить 10000 объектов, выполняется обход базы данных в 10000, что необходимо найти способ уменьшить.

Возможное решение:

  • Использование библиотеки, такой как EF Extensions , которая обеспечивает массовые функции, такие как Bulk Merge.

Отказ от ответственности : я владелец проекта Расширения Entity Framework

Эта библиотека не является бесплатной, но распространяется на ваш DbContext с методами массового расширения:

  • Bulk SaveChanges
  • Пакетное сохранение изменений
  • Массовая вставка
  • Массовое удаление
  • Массовое обновление
  • Bulk Merge

В вашем случае вы ищете Bulk Merge, который обновит существующие сущности и вставит не соответствующие.

Пример

// Easy to use
context.BulkMerge(customers);

// Easy to customize
context.BulkMerge(invoices, options => options.IncludeGraph = true);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...