Долгосрочный запрос в Entity Framework с несколькими объединениями таблиц - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть запрос, который объединяет около 10 таблиц, некоторые из которых являются самообращающимися таблицами. Я использую оператор IN для условного в столбце идентификатора (индексируется) самой верхней таблицы.

var aryOrderId = DetermineOrdersToGet(); //Logic to determine what orderids to get
var result = dbContext.Orders.Where(o=>aryOrderId.Contains(o.id)
    .Include(o=>o.Customer)
    .Include(o=>o.Items.Select(oi=>oi.ItemAttributes))
    .Include(o=>o.Items.Select(oi=>oi.Dimensions))
    .Include(o=>o.CustomOptions.Select(oc => oc.CustomOptions1))
.....A Bunch more.....
    .ToList();

Я хотел бы найти способ ускорить это, не изменяя дизайн моих таблиц и не выравнивая структуру. В настоящее время 50-200 записей занимают 10-20 секунд.

Эти данные могут быть только для чтения. Мне не нужно обновлять эти записи.

  1. Можно ли преобразовать это в хранимую процедуру?

  2. Насколько сложно это сделать?

  3. Смогу ли я получить заметное повышение производительности?

Ответы [ 3 ]

0 голосов
/ 06 ноября 2018

Одна из более медленных частей запроса к базе данных - это передача выбранных данных из СУБД в локальный процесс. Следовательно, разумно выбирать только те свойства, которые вы действительно планируете использовать.

Например, кажется, что Order имеет ноль или более ItemAttributes. Evey ItemAttribute принадлежит ровно одному Order, используя внешний ключ OrderId.

Если вы получите все Orders с Id в ArryOrderId, каждый ордер с его тысячой ItemAttributes, вы знаете, что каждый ItemAttribute будет иметь внешний ключ OrderId с тем же значением, что и Id Заказ, к которому он принадлежит. Отправлять 1000 раз одно и то же значение бесполезно.

При запросе данных с использованием структуры сущностей всегда используйте Select. Выберите только те свойства, которые вы действительно планируете использовать. Используйте «Включить» только в том случае, если вы хотите изменить извлеченные объекты.

var result = dbContext.Orders
    .Where(order=>aryOrderId.Contains(order.id)
    .Select(order => new
    {   // select only the properties you plan to use:
        Id = order.Id,
        ...
        Customer = order.Customer.Select(customer => new
        {   // again: only the properties you plan to use
            Id = order.Customer.Id,
            Name = order.Customer.Name,
            ...
        },
        ItemAttributes = order.ItemAttributes.Select(itemAttribute => new
        {
            ...
        })
        .ToList(),
        Dimensions = order.Dimensions.Select(dimension => new
        {
            ...
        })
        .ToList(),
        ....A Bunch more.....
})
.ToList();

Если после выбора только тех свойств, которые вы действительно планируете использовать, запрос по-прежнему занимает слишком много времени, подумайте еще раз: действительно ли мне нужны все эти свойства.

Другим решением для ограничения времени выполнения является получение даты «на страницу» с помощью Skip / Take. Разумеется, существует опасность, что при просмотре страницы 10 данные страницы 1 могут быть изменены таким образом, что страница 10 должна интерпретироваться по-разному.

0 голосов
/ 06 ноября 2018

Отвечая на ваши 3 вопроса. Да, вы можете использовать хранимую процедуру, и это то, что я бы сделал в этой ситуации. Это совсем не сложно; EF делает это довольно просто. Вы можете либо вернуть новый сложный тип, либо сопоставить его с сущностью. Поскольку вы говорите, что данные могут быть только для чтения, вы, вероятно, в порядке с базовым импортированием функции, возвращающей сложный тип (поведение EF по умолчанию). В любом случае вы получите заметный прирост производительности.

Для db-first см. http://www.entityframeworktutorial.net/stored-procedure-in-entity-framework.aspx

По сути, вы будете следовать этим шагам.

  1. Создать хранимую процедуру в вашей базе данных
  2. Обновить модель из базы данных. Когда он спросит, какие объекты нужно включить, вы сможете выбрать хранимую процедуру.
  3. Нажмите Готово. EF сгенерирует сложный тип, который имеет все свойства, возвращаемые вашей хранимой процедурой, и добавит подпись в ваш контекст для выполнения хранимой процедуры, так что ее можно вызвать так: var results = myContext.myProcedure(param1, param2); Есть скриншоты этого на ссылка выше.
  4. Вы также можете войти и изменить модель, чтобы настроить детали, такие как имя сложного типа и имя функции (по умолчанию функция будет соответствовать имени SP и вернет ObjectResult<T> где T - ваш сложный тип, который будет именем процедуры с суффиксом "_Result").
0 голосов
/ 06 ноября 2018

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

т.е.

var query = dbContext.Orders
  .Where(x => aryOrderId.Contains(x => x.OrderId))
  .Select(x => new 
  {
     x.OrderId,
     x.OrderNumber,
     OrderItems = x.Items.Select(i => new 
     {
       i.ItemId,
       Attributes = i.Attributes.Select(a => a.AttributeName).ToList(),
       Dimensions = i.Dimensions.Select(d => new {d.DimensionId, d.Name}).ToList(),
     }).ToList(),
     // ...
   }).ToList();

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

В качестве альтернативы вы можете рассмотреть возможность использования представления в базе данных и привязки объекта к представлению. Эта опция хорошо работает только для чтения данных. При условии, что вы включаете соответствующие идентификаторы, вы всегда можете в любое время извлечь применимые «реальные» объекты, чтобы загрузить страницу сведений или выполнить действие / обновление для объекта.

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