Подтвердить что ты не робот

Сколько Include я могу использовать в ObjectSet в EntityFramework для сохранения производительности?

Я использую следующий запрос LINQ для моей страницы профиля:

var userData = from u in db.Users
                        .Include("UserSkills.Skill")
                        .Include("UserIdeas.IdeaThings")
                        .Include("UserInterests.Interest")
                        .Include("UserMessengers.Messenger")
                        .Include("UserFriends.User.UserSkills.Skill")
                        .Include("UserFriends1.User1.UserSkills.Skill")
                        .Include("UserFriends.User.UserIdeas")
                        .Include("UserFriends1.User1.UserIdeas")
                               where u.UserId == userId
                               select u;

Он имеет длинный графический объект и использует множество включений. Он работает идеально сейчас, но когда сайт имеет много пользователей, это сильно повлияет на производительность?

Должен ли я сделать это другим способом?

4b9b3361

Ответ 1

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

Предположим, что у нас есть сущность Customer (Id, Name, Address) и сущность Order (Id, CustomerId, Date). Теперь мы хотим запросить клиента с ее заказами:

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == 1);

Результирующий набор данных будет иметь следующую структуру:

 Id | Name | Address | OrderId | CustomerId | Date 
---------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1.
  1 |  A   |   XYZ   |    2    |     1      | 2.1.

Это означает, что Cutomers данные повторяются для каждого Order. Теперь давайте расширим пример с помощью других объектов - "OrderLine" (Id, OrderId, ProductId, Quantity) and Product (Id, Name) `. Теперь мы хотим запросить клиента с ее заказами, заказами и продуктами:

var customer = context.Customers
                      .Include("Orders.OrderLines.Product")
                      .SingleOrDefault(c => c.Id == 1);

Результирующий набор данных будет иметь следующую структуру:

 Id | Name | Address | OrderId | CustomerId | Date | OrderLineId | LOrderId | LProductId | Quantity | ProductId | ProductName
------------------------------------------------------------------------------------------------------------------------------
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     1       |    1     |     1      |    5     |    1      |     AA
  1 |  A   |   XYZ   |    1    |     1      | 1.1. |     2       |    1     |     2      |    2     |    2      |     BB
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     3       |    2     |     1      |    4     |    1      |     AA
  1 |  A   |   XYZ   |    2    |     1      | 2.1. |     4       |    2     |     3      |    6     |    3      |     CC

Как вы можете видеть, данные становятся довольно много дублируемыми. Общее число каждого из них относится к правильной навигации (Product в примере) добавит новые столбцы и каждый из них включит в свойство навигации коллекции (Orders и OrderLines в примере) добавит новые столбцы и дублирует уже созданные строки для каждая строка в включенной коллекции.

Это означает, что ваш пример может легко содержать сотни столбцов и тысячи строк, для передачи которых требуется много данных. Правильный подход - это создание тестов производительности, и если результат не удовлетворит ваши ожидания, вы можете изменить свой запрос и загрузить свойства навигации отдельно своими собственными запросами или методом LoadProperty.

Пример отдельных запросов:

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == 1);
var orderLines = context.OrderLines
                        .Include("Product")
                        .Where(l => l.Order.Customer.Id == 1)
                        .ToList();

Пример LoadProperty:

var customer = context.Customers
                      .SingleOrDefault(c => c.Id == 1);
context.LoadProperty(customer, c => c.Orders);

Также вы всегда должны загружать только те данные, которые вам действительно нужны.

Изменить: Я только что создал предложение

Ответ 2

Вы можете повысить производительность многих из них , создав два или более небольших запроса данных из базы данных, как показано ниже.

По моему опыту, только может дать максимум 2 включает в запрос, как показано ниже. Больше, чем это даст очень плохую производительность.

var userData = from u in db.Users
                        .Include("UserSkills.Skill")
                        .Include("UserIdeas.IdeaThings")
                        .FirstOrDefault();

 userData = from u in db.Users
                    .Include("UserFriends.User.UserSkills.Skill")
                    .Include("UserFriends1.User1.UserSkills.Skill")
                    .FirstOrDefault();

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

Я написал сообщение в блоге выше этого, используя собственный опыт. Здесь

Надеюсь, это поможет вам.

Ответ 3

Да, будет. Избегайте использования Include, если он расширяет несколько строк данных в строке мастер-таблицы.

Я считаю, что EF преобразует запрос в одно большое объединение вместо нескольких запросов. Таким образом, вы будете дублировать данные главной таблицы по каждой строке таблицы подробностей.

Например: Мастер → Детали. Скажем, у мастера есть 100 строк, у деталей есть 5000 строк (50 для каждого мастера).

Если вы lazy-load детали, вы возвращаете 100 строк (размер: мастер) + 5000 строк (размер: подробности).

Если вы используете .Include( "Подробности" ), вы возвращаете 5000 строк (размер: мастер + детали). По существу, основная часть дублируется более 50 раз.

Он умножается вверх, если вы включаете несколько таблиц.

Проверьте SQL, сгенерированный EF.

Ответ 4

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

Ответ 5

Результат include может измениться: он зависит от объекта, который вызывает метод include.

Как и пример, предложенный Ладиславом Мрнкой, предположим, что у нас есть сущность

Клиент (идентификатор, имя, адрес)

которые отображаются в этой таблице:

Id  |  Name   | Address
-----------------------
C1  |  Paul   |   XYZ   

и сущность Order (Id, CustomerId, Total)

которые отображаются в этой таблице:

Id |  CustomerId  | Total
-----------------------
O1 |      C1      |  10.00
O2 |      C1      |  13.00

Отношение один клиент к многим ордерам


Эскаль 1: Клиент = > Заказы

var customer = context.Customers
                      .Include("Orders")
                      .SingleOrDefault(c => c.Id == "C1");

Linq будет переведен в очень сложный sql-запрос.

В этом случае запрос будет генерировать две записи, и информация о клиенте будет реплицирована.

 Customer.Id   |   Customer.Name |    Order.Id |  Order.Total
-----------------------------------------------------------
     C1        |       Paul      |       O1    |    10.00     
     C1        |       Paul      |       O2    |    13.00   

Эскаль 2: Заказ = > Клиент

var order = context.Orders
                      .Include("Customers")
                      .SingleOrDefault(c => c.Id == "O1");

Linq будет переведен в простой sql Join.

В этом случае запрос будет производить только одну запись без дублирования информации:

 Order.Id |  Order.Total |  Customer.Id   |   Customer.Name
-----------------------------------------------------------
     O1   |    10.00     |      C1        |       Paul