Сколько Включить я могу использовать для ObjectSet в EntityFramework, чтобы сохранить производительность? - PullRequest
47 голосов
/ 02 апреля 2011

Я использую следующий запрос 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;

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

Ответы [ 5 ]

84 голосов
/ 02 апреля 2011

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

Предположим, у нас есть сущность 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, Количество) 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);

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

Редактировать: Я простосоздано предложение для Data UserVoice для поддержки дополнительной стратегии активной загрузки, в которой загруженные данные будут передаваться в дополнительном наборе результатов (создается отдельным запросом в рамках одного и того же обращения к базе данных).Если вы находите это улучшение интересным, не забудьте проголосовать за предложение.

15 голосов
/ 13 декабря 2012

Вы можете улучшить производительность многих включений , создав 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();

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

У меня естьнаписано в блоге выше, используя мой собственный опыт. Это Здесь

Я надеюсь, что это поможет вам.

8 голосов
/ 02 апреля 2011

Да, будет. Избегайте использования Включить, если оно расширяет несколько строк подробностей в строке основной таблицы.

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

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

Если вы лениво загружаете детали, вы возвращаете 100 строк (размер: мастер) + 5000 строк (размер: детали).

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

Увеличивается, если вы включаете несколько таблиц.

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

3 голосов
/ 02 апреля 2011

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

2 голосов
/ 27 ноября 2013

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

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

Клиент (Id, Имя, Адрес)

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

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

и Заказ объекта (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.

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

 Order.Id |  Order.Total |  Customer.Id   |   Customer.Name
-----------------------------------------------------------
     O1   |    10.00     |      C1        |       Paul    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...