Можно ли оптимизировать этот EF-запрос?

#c# #entity-framework #entity-framework-4 #linq-to-entities

#c# #entity-framework #entity-framework-4 #linq-to-entities

Вопрос:

Я новичок в Entity Framework и не уверен, когда использовать ускоренную загрузку и Include , когда использовать запросы LINQ с Select , а когда загружать объекты по требованию. Я также не знаю, кэширует ли EF объекты и когда именно. (Да, я в конце концов прочитаю документы 🙂

Я создаю систему обмена сообщениями, встроенную в ASP.Веб-сайт NET MVC, и производительность будет действительно важна. Представьте следующий сценарий.

Когда Рианна заходит в свой почтовый ящик, ей нужно увидеть список сообщений, в каждом из которых есть имя отправителя и фотография. Представьте, что Джон написал слова Рианны к своей новой песне. В ее папке «Входящие» есть сообщение с фотографией Джона. Когда Джон заходит в свой почтовый ящик «Исходящие», он видит то же сообщение с именем и фотографией Рианны.

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

 private static Func<MyEntities, int, Message> _getMessageByIdQuery = CompiledQuery.Compile(
    ( MyEntities ctx, int messageId ) => ctx.Messages
        .Include( "SenderUser" )
        .Include( "SenderUser.UserProfile.Image" )
        .Include( "RecipientUser" )
        .Include( "RecipientUser.UserProfile.Image" )
        .SingleOrDefault( message => message.ID == messageId ) );
  

Будет ли этот запрос эффективным? Есть ли какой-либо способ загрузить SenderUser.UserProfile.Image без загрузки SenderUser , учитывая, что User.ID и UserProfile.UserID являются взаимно однозначными? Я полагаю, мне нужно было бы использовать LINQ JOIN для этого. Правильно ли это?

Эффективнее ли использовать LINQ, чтобы избежать загрузки всех полей, когда требуется только одно (например, загружать только ImageID из UserProfile )?

Не лучше ли сделать то же самое, используя Load где-нибудь в коде?

Отправляется ли такой запрос в базу данных каждый раз? Представьте, что у нас была тысяча сообщений, все от одного и того же человека. Будем ли мы запрашивать его Image каждый раз?

Я использую EF 4 и ASP.NET MVC 3.

Спасибо.

Ответ №1:

Одним из способов было бы отправить пользовательские объекты отображения / данных непосредственно из запроса, таким образом, вам не нужно загружать дополнительные объекты. Пример:

 var dto = ctx.Messages
          .Where(whereclause)
          .Select(m=> new DTOTYPE
                         {Id = m.Id, 
                          Content= m.Content, 
                          SenderId = m.SenderUser.Id, 
                          SenderImage = m.SenderUser.UserProfile.Image, 
                          SenderImageId = m.SenderUser.UserProfile.ImageId}) 
                         //more fields if you need any
  

Хотя это будет зависеть от вашего сценария, но таким образом вам не придется делать сумасшедшие включения повсюду. Но вам нужно будет управлять вашими объектами view / data, поскольку они могут выйти из-под контроля для многих разных экранов и представлений. Надеюсь, это поможет.

Комментарии:

1. Спасибо. В конце концов, я попробовал использовать как ручные, join так и проекционные, Select и, похоже, Select что это создает немного более тяжелые соединения, но гораздо более удобен в обслуживании, поэтому я оставлю это как есть.

Ответ №2:

Используйте проекцию, как предложено с помощью @AD.Net . При использовании проекции вы можете передавать данные, которые вам действительно нужны, вместо целых объектов, полных свойств, которые вы никогда не используете. Имейте в виду, что это действительно загружает изображения каждый раз, когда вы запрашиваете сообщение в базе данных.

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

Комментарии:

1. Спасибо за ответ! Представьте, что Джон часто отправляет сообщения Рианне. Она получила бы почтовый ящик, полный его сообщений (с его фотографией!) но неизвестно, что его изображение будет повторяться до тех пор, пока не будет загружен список.

2. Итак, сначала загрузите сообщения, а затем изображения всех отправителей в отдельном запросе. В противном случае вы можете получать 100 сообщений от одного и того же пользователя и загружать его изображение 100 раз.

Ответ №3:

Никто не может сказать вам, как будет работать ваш код, вам придется выполнять бенчмарк. Да, вы можете избежать загрузки SenderUser, если используете соединение. Посмотрите сюда, если хотите знать, как просмотреть сгенерированный sql-код.