Отключение отложенной загрузки предотвратит проблемы с производительностью 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) потребуется из данных.