Запрос выполняется несколько раз и странно сделан - PullRequest
0 голосов
/ 12 марта 2020

Я использую Application Insights в своем C# MVC веб-приложении. Переходя к деталям сквозной транзакции для долго выполняющегося запроса .. Я получаю, что следующий запрос выполняется более 10 раз подряд на медленной странице.

SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[paid] AS [paid]
    FROM (SELECT 
    [TableXX].[ID] AS [ID], 
    [TableXX].[paid] AS [paid]
    FROM [dbo].[TableXX] AS [TableXX]) AS [Extent1]
    WHERE [Extent1].[ID] = @EntityKeyValue1

Я не уверен, как найти его в моем приложении. Но также я не знаю, почему мой TableXX подзапрашивается с предложением no where для двух полей, если это имеет смысл.

Кто-нибудь сталкивался с чем-то подобным? Может ли это быть связано с неправильным использованием .Include или просто как EF структурирует определенный тип запахов кода Linq?

Может ли это быть?

db.TableParent.Where(li => li.itemID == 16 && li.TableChild.TableXX != null && li.TableChild.companyId == company.id).ToList();

ОБНОВЛЕНИЕ:

Поэтому я использовал Database.Log и точки останова для локального запуска своего веб-сайта и нахождения запроса, вызывающего проблему.

Я создаю следующее и передаю его функции, которая выполняет второй запрос ниже. Почему ToList на первом по-прежнему позволяет второму загружать их один за другим?

List<TableParentItem> currentTableParentItems = db.TableParent.Where(i => i.id == givenId).Include(i => i.TableChild).Include(i => i.TableXX).GroupBy(i => new { i.id1, i.id2}).SelectMany(location => location.OrderByDescending(i => i.order).Take(1)).OrderBy(i => i.StartDate).ThenByDescending(i => i.id2).ToList();

..

result = currentTableParentItems.Where(i => i.Remaining > 0 && i.dueDate <= DateTime.Now).ToList();

Где i.Remaining - это get {return TableXX. осталось; }

.. Я также запутался, потому что, когда я устанавливаю точку останова до и после переменной currentTableParentItems выше .. Я получаю несколько запросов: 1 в TableXX, 1 в TableParent и 2 в TableChild ... и затем пятый случайная таблица, на которую нет ссылок в этой строке.

1 Ответ

0 голосов
/ 12 марта 2020

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

Не видя реального кода и выполняемых запросов, используя следующий пример:

db.TableParent.Where(li => li.itemID == 16 && li.TableChild.TableXX != null && li.TableChild.companyId == company.id).ToList();

Если соответствующий контроллер MVC возвращает это Возвращаем к представлению коллекцию «Parents», сериализатор будет перебирать каждого родителя, смотреть ссылку на TableChild (вместе с любыми другими ссылками для детей, внуков и т. Д. c. Et c.) И когда он касается этих свойства, если DbContext еще не разрешил их, ссылки будут лениво загружаться по одному.

Простое решение в этом случае состоит в добавлении .Include(x => x.TableChild) к запросу до .Where() пункт. В долгосрочной перспективе было бы полезно смотреть на отправляющие модели представлений, а не на сущности, чтобы избежать неожиданных неожиданностей, подобных этой.

Обновление: GroupBy не проходит через выражения Include в EF. Вам нужно либо выполнить GroupBy в памяти (дорого), либо использовать GroupBy для определения идентификаторов родителей, а затем загрузить родителей с их загруженными отношениями. В качестве альтернативы, если вы можете проецировать ваши ViewModels в выражение запроса, вы сможете составлять модели представлений, не стремясь загрузить отношения.

Пример 1: Получить идентификаторы, а затем загрузить:

List<int> currentTableParentItemIds = db.TableParent
    .Where(i => i.id == givenId)
    .GroupBy(i => new { i.id1, i.id2})
    .SelectMany(location => location
        .OrderByDescending(i => i.order)
        .Take(1))
    .Select(i => i.id) // Assuming this gives you the ID of the records you want.
    .ToList();

List<TableParentItem> currentTableParentItems = db.TableParent
    .Include(i => i.TableChild)
    .Include(i => i.TableXX)
    .Where(i => currentParentItemIds.Contains(i.id))
    .OrderBy(i => i.StartDate)
    .ThenByDescending(i => i.id2)
    .ToList();

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

Пример 2: Если эта загрузка должна заполнить модель представления, то вам лучше снять загрузочную нагрузку Includes и проецировать ваш вид моделируется непосредственно в выражении запроса, используя Select или Automapper's ProjectTo. Это может быть немного рефакторингом, если вы используете фабричные методы или такие для заполнения моделей представлений, так как они не проецируются через EF до SQL.

List<ParentItemViewModel> currentTableParentItems = db.TableParent
    .Where(i => i.id == givenId)
    .GroupBy(i => new { i.id1, i.id2})
    .SelectMany(location => location
        .OrderByDescending(i => i.order)
        .Take(1))
    .Select(i => new ParentItemViewModel
    {
       id = i.Id,
       ChildName = i.TableChild.Name, // etc. Use whatever related tables needed. No need to eager load.
       // ...
    }).ToList();

Преимущество этого подхода заключается в том, что что он генерирует наиболее оптимальный SQL. Вместо того, чтобы возвращать все родительские столбцы и все столбцы связанных объектов, SQL, сгенерированный этим выражением, вернет только то, что необходимо для заполнения модели представления. Также нет риска отложенных загрузок до тех пор, пока вы не внедрите какие-либо объекты в модель представления. (Т. Е. Child = i.TableChild = плохо)

Если код для возврата сущностей изолирован в чем-то вроде репозитория и возвращает что-то вроде IEnumerable<ParentTable>, то для принятия оптимизаций, подобных примеру 2, можно облегчить принятие типа возврата IQueryable<ParentTable>, чтобы потребители могли уточнить запросы EF для проецирования их конкретных c потребностей. В противном случае, вариант № 1 должен привести вас туда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...