Должны ли мы отключить отложенную загрузку Entity Framework в веб-приложениях? - PullRequest
0 голосов
/ 27 января 2019

Я слышал, что вам нужно отключить функцию отложенной загрузки EF в веб-приложениях. (ASP.NET). Здесь и здесь , например.

Теперь я в замешательстве, потому что я всегда думал, что отложенная загрузка всегда должна быть включена. Итак, теперь мой вопрос: действительно ли лучше отключить ленивую загрузку в веб-приложениях с точки зрения производительности. Если да, может ли кто-нибудь объяснить причины и упомянуть плюсы и минусы?

Ответы [ 6 ]

0 голосов
/ 21 марта 2019

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

Представьте себе сценарий Master => Details в веб-приложении, и к тому времени, как вы поняли, что пользователи не так уж заинтересованы в деталях. (Вы можете анализировать и проверять запросы, инициированные пользователями). Там не было бы необходимости загружать детали для каждой основной записи заранее.

С другой стороны, если детали являются основной частью, с которой нужно взаимодействовать, просто сделайте загрузку и извлеките всю Master => Detail для каждого.

Помимо отложенной загрузки, убедитесь, что:

Всегда разрешать подкачку на стороне сервера и загружать ограниченные данные / Загружать данные по запросу пользователя при их переходе от страницы к странице.

Если в основном есть запросы на чтение, отключите AutoTracking.

0 голосов
/ 20 марта 2019

Я думаю, вам нужно начать с того, чтобы задать несколько вопросов, прежде чем выбрать один и отклонить другой:

  1. Насколько велик мой набор данных, и мне немедленно нужны соответствующие данные?(Это опять же зависит от ваших потребностей)
  2. Как уже говорили другие люди о проблеме N + 1;однако, это становится важным в зависимости от размера вашего набора данных.
  3. Будет ли прагматичным иметь двустороннюю поездку на сервер для получения связанных данных?
  4. Тогда нужны данные.Хотите ли вы его в реальном времени или в кешированной версии?

Мой вклад во все важные вклады других.

0 голосов
/ 18 марта 2019

Отключение отложенной загрузки предотвратит проблемы с производительностью Select N + 1, а также рекурсивную сериализацию, однако она заменит их другим артефактом - нулевыми ссылками.

При работе с веб-приложениями я не отключаю отложенную загрузку, а скорее гарантирую, что мои контроллеры / API не возвращают сущности, а скорее возвращают ViewModels или DTO. Когда вы применяете классы POCO для подачи в ваши представления только объема данных и нужной им структуры и используете .Select() или Automapper's ProjectTo<TViewModel>() для их заполнения с помощью отложенного выполнения, вы избегаете необходимости беспокоиться о отложенной загрузке и улучшить общую производительность и использование ресурсов для вашего приложения. Технически, используя этот подход, отложенная загрузка может быть отключена, так что на самом деле это не аргумент за или против ее отключения, а то, что простое отключение отложенной загрузки не сделает ваше веб-приложение «лучше».

Принятие ViewModels предлагает ряд преимуществ:

  • Избегает отложенных вызовов при загрузке или неожиданных #null ссылок.
  • Отправляет только данные, необходимые представлению / потребителю, и ничего более. (Меньше данных по проводам и меньше информации для хакеров с отладочными представлениями.)
  • Создает эффективные, индексируемые запросы к серверу базы данных.
  • Предоставляет место для вычисляемых столбцов, которое не сработает в EF.
  • Помогает уменьшить проблемы безопасности (неожиданные изменения объекта) в обратном пути. (Модель представления не может быть просто повторно присоединена и передана в БД ленивым разработчиком)

Так что для меня проблема с веб-приложениями заключается не в том, чтобы загружать или нет, а в том, чтобы просто не передавать сущности клиенту. Я вижу слишком много «примеров», где они передают сущности. Я не считаю это здоровым образцом.

В качестве примера, используя модель представления, первый вопрос: «Какие данные нужны моему представлению?» Таким образом, учитывая продукт и категорию продукта, если я хочу отправить сущность продукта, но мне также нужно имя категории продукта, например, мы сталкиваемся с неприятной проблемой, если наша категория продуктов содержит набор продуктов, и каждый из этих продуктов имеет категория. Когда мы передаем наш продукт сериализатору, он попадает в эту циклическую ссылку и либо спасается (оставляя ссылку или коллекцию #null), либо генерирует исключение. Но работая с помощью Select N + 1, мы перебираем свойства Product, нажимаем ссылку ProductCategory, затем «SELECT FROM ProductCategory WHERE ProductCategoryID = 3». Затем, когда мы перебираем эту категорию продукта, мы нажимаем другую ссылку, и это еще один SELECT .... и так далее по цепочке.

Используя модель представления, вы ограничиваете данные, которые вы хотите получить, только тем, что нужно представлению. Я создаю модель представления продукта, которая описывает поля, которые меня интересуют, независимо от того, откуда поступают данные. Если я хочу что-то похожее на продукт, его название и название категории:

public class ProductViewModel
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public string CategoryName { get; set; }
}

затем загрузить его:

var viewModel = context.Products
    .Where(x => x.ProductId == productId) 
    .Select(x => new ProductViewModel
    {
        ProductId = x.ProductId,
        ProductName = x.Name,
        CategoryName = x.Category.Name
    }).Single();

Готово. Никаких ленивых нагрузок или активных нагрузок не требуется. Это создает один запрос, который возвращает одну запись с тремя нужными столбцами. (Вместо всей записи о товаре и его категории)

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

0 голосов
/ 16 марта 2019

Ленивая загрузка создаст проблему N + 1.

Что это значит?

Это означает, что он попадет в БД один раз (по крайней мере) для каждого загруженного объекта + начальный запрос.

Почему это плохо?

Предположим, у вас есть эти классы Movie и MovieGenre, и у вас есть 100 фильмов с 30 жанрами между ними в БД

public class Movie
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public virtual MovieGenre MovieGenre { get; set; }

        public byte MovieGenreId { get; set; }
}

public class MovieGenre
    {
        public byte Id { get; set; }

        public string Name { get; set; }
    }

Теперь предположим, что вы переходите на страницу, на которой будут показаны все 100 фильмов ( помните 30 жанров фильма? ), БД выполнит 31 запрос (30 для каждого жанра фильма)+ 1 для фильмов) и запросы будут выглядеть примерно так:

Начальный запрос (+1 часть):

SELECT Id, Name, MovieGenreId
From Movie

Дополнительные запросы (N часть): - 1 SELECT Id, NameИз MovieGenre, где Id = 1

-- 2
SELECT Id, Name
From MovieGenre
Where Id = 2

-- 3
SELECT Id, Name
From MovieGenre
Where Id = 3

-- 4
SELECT Id, Name
From MovieGenre
Where Id = 4
.
.
.

Стремительная загрузка избегает всего этого беспорядка и будет использовать один запрос с правильными объединениями.

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

0 голосов
/ 27 января 2019

У меня был один устаревший проект.Я был очень удивлен, когда я проверял в профиле sql, сколько запросов отправляется в базу данных.Это было около 180 (!) Для домашней страницы!На домашней странице есть только два списка по 20-30 пунктов в каждом.Итак, вы должны очень хорошо понимать N + 1 запросы.Вы должны внимательно проверить код, пересмотреть его.Для меня ленивая загрузка доставляет много проблем.Вы никогда не знаете, сколько запросов поступает в базу данных при использовании этой функции.

0 голосов
/ 27 января 2019

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

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