Entity Framework 6 - производительность запросов - PullRequest
2 голосов
/ 21 января 2020

Я использую Entity Framework 6, и в настоящее время у меня есть запрос со многими включениями, который загружает около 1200 сущностей в dbContext. Загрузка объектов кажется довольно медленной, так как запрос занимает почти минуту. Могу ли я что-нибудь сделать с представлением? У меня есть 4 таких запроса, которые требуют 2,5 минуты для загрузки? LazyLoading включен, но по соображениям производительности я предварительно загружаю объекты.

var report = DbContext.REPORT.Single(r => r.ID == reportId);

//this query takes a bit less than 1 minute
DbContext.REPORT_ELEMENT
    .Include(re => re.LAYOUT)
    .Include(re => re.PAGEMASTER)
    .Include(re => re.REPORT_ELEMENTS)
    .Include(re => re.SUBTITLE_CONTENT)
    .Include(re => re.REPORT_ELEMENT_NOTE)
    .Include("SUBTITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Include("TITLE_CONTENT.CONTENT_ELEMENT.LANGUAGE")
    .Where(re => re.REPORT_ID == report.ID)
    .Load();

Ответы [ 3 ]

1 голос
/ 21 января 2020

Предложения по производительности:

  • Запрет отслеживания. Запрос в режиме только для чтения.
  • Не допускайте получения слишком большого количества данных в одном запросе. Попробуйте на странице.
  • Предотвратить включение. В запросе слишком много Include с, что ухудшает производительность.

Запретить отслеживание

Попробуйте добавить AsNoTracking, чтобы повысить производительность запроса.

Ссылка : https://docs.microsoft.com/en-us/ef/core/querying/tracking#no -tracking-query

Получите только те данные, которые вам нужны

И основная причина медленного запроса - вывод слишком большого количества данных. Попробуйте добавить: Take(200), Skip(), чтобы получить только те данные, которые вам нужны или требуются текущей странице. Используйте пейджер для генерации отчета. Это может очень помочь.

Запрет Include

Include создает SQL для выбора нескольких таблиц. Что значительно увеличило сложность. Вы можете выбрать только те данные, которые вам нужны, и запретить написание функции Include.

Например, если вы хотите получить только последний шарик в коробке, рассмотрите возможность записи так:

public class Box
{
    public int Id { get; set; }
    public IEnumerable<Ball> Balls { get; set; }
}

public class Ball
{
    public int Id { get; set; }

    public int BoxId { get; set; }
    public Box Box { get; set; }
}
var boxes = await Boxes
            // DO NOT Call Include(t => t.Balls) here!
            .Where(somecondition)
            .Select(t => new Box(){
              Id = t.Id,
              Balls = t.Balls.OrderByDescending(x => x.CreationTime)
                         .Take(1) // Only get what you need
            })               
            .ToListAsync()

Также, когда мы используем Select, мы можем удалить .Include, потому что это не будет иметь никакого эффекта.

0 голосов
/ 21 января 2020

Отказ от ответственности : я являюсь владельцем проекта Entity Framework Plus

Функция Query IncludeOptimized позволяет фильтровать с включением и оптимизировать производительность запроса на в то же время.

Обычно повышает производительность (разделить запрос на более мелкие запросы)

DbContext.REPORT_ELEMENT
    .IncludeOptimized(re => re.LAYOUT)
    .IncludeOptimized(re => re.PAGEMASTER)
    .IncludeOptimized(re => re.REPORT_ELEMENTS)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT)
    .IncludeOptimized(re => re.REPORT_ELEMENT_NOTE)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT))  // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .IncludeOptimized(re => re.TITLE_CONTENT)
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT)) // SelectMany?
    .IncludeOptimized(re => re.SUBTITLE_CONTENT.Select(sc => sc.CONTENT_ELEMENT).Select(ce => ce.LANGUAGE)) // SelectMany?
    .Where(re => re.REPORT_ID == report.ID)
    .Load();

Документация: EF + Query IncludeOptimized

0 голосов
/ 21 января 2020

В дополнение к советам от Anduin, я хотел бы добавить совет, чтобы разделить Includes() на несколько различных запросов. EF сможет отслеживать ссылки между сущностями в одном и том же DBContext. Как правило, не используйте более трех Includes() в одном запросе. Также убедитесь, что у вас есть индекс в БД для каждого результирующего JOIN.

Чтобы это можно было сделать, вы должны выставить свои поля FK в сущностях в дополнение к свойствам навигации.

Ваш начальный запрос будет выглядеть примерно так:

    DbContext.LAYOUT
        .Where(re => re.LAYOUT_ID == report.LAYOUT_FK)
        .Load();
    DbContext.PAGEMASTER
        .Where(re => re.PAGEMASTERT_ID == report.PAGEMASTER_FK)
        .Load();
...