EF Core Filtered Include: "Лямбда-выражение, используемое внутри Include, недействительно"
Я пытаюсь использовать очень простую функцию, используя новую функцию "Отфильтрованное включение" с EF Core 5.0.0,
https://docs.microsoft.com/en-us/ef/core/querying/related-data
но я столкнулся с проблемой. Выдает следующую ошибку:
System.InvalidOperationException: Lambda expression used inside Include is not valid.
Вот фрагмент кода, который его вызывает. По документации считаю, что моя логика верна. Я не уверен, почему это происходит. Что-то мне не хватает?
List<Vendor> vendors = context.Vendors
.Include(v => v.Items.Where( i => i.Rating > 1)
.ToList();
Поставщики - это родитель элементов. Это очень простая установка. Отношение "один (продавец) ко многим (предмет)".
1 ответ
Обычно более эффективно использовать Select вместо Include. Используя Select, вы можете выбрать свойства, которые планируете использовать. Включить переносит весь элемент, включая свойства, которые вы не будете использовать.
Рассмотрим школу со студентами в прямом отношении "один ко многим": в каждой школе ноль или более учеников, каждый ученик посещает ровно одну школу, а именно школу, на которую ссылается внешний ключ SchoolId.
Предположим, вы хотите запросить школу [10] с 2000 учениками. Каждый ученик школы [10] будет иметь значение для внешнего ключа SchoolId, равное 10. Если вы используете Include, вы передадите это значение примерно 2000 раз, пока вы уже знаете значение. Какая трата вычислительной мощности.
При запросе данных всегда используйте Select и выбирайте только те свойства, которые вы действительно планируете использовать. Используйте "Включить" только в том случае, если вы планируете изменить включенные данные.
var result = context.Vendors.Select(vendor => new
{
// Select only the Vendor properties that you intend to use:
Id = vendor.Id,
Name = vendor.Name,
...
HighRatedItems = vendor.Items.Where(item => item.Rating > 1)
.Select(item => new
{
// again: select only the properties that you plan to use
Id = item.Id,
...
// not needed, you know the value:
// VendorId = item.VendorId,
})
.ToList(),
})
.ToList();
Здесь я использую автоматические типы. Если вам действительно нужны поставщики:
var result = context.Vendors.Select(vendor => new Vendor
{
// Select only the Vendor properties that you intend to use:
Id = vendor.Id,
Name = vendor.Name,
...
Items = vendor.Items.Where(item => item.Rating > 1)
.Select(item => new Item
{
// again only the items that you plan to use.
Id = item.Id,
...
})
.ToList(),
});
Имейте в виду, что некоторые свойства не будут иметь правильного значения. Это может сбить с толку пользователей вашего метода. Но опять же: они знают, что они все равно не получат "Продавцов со своими товарами", потому что вы не вернете все их товары, поэтому они уже не ожидают полных объектов Продавца.
Чтобы решить проблему неполного заполнения возвращаемых объектов, рассмотрите возможность реализации шаблона репозитория. отделите типы таблиц в вашей базе данных от фактически возвращаемых данных.
Для этого потребуются дополнительные классы, которые кажутся очень похожими на ваши оригинальные Продавцы и Предметы. Тем не менее, у него есть то преимущество, что пользователи будут точно знать, что они получают. Однако преимущества заключаются в том, что вашим пользователям не нужно менять код, если вы решите изменить макет своей базы данных. Кроме того, репозиторий скрывает, что вы используете базу данных. Это может быть файл CSV или Json, а может быть, даже просто словарь. Это значительно упрощает модульное тестирование ваших пользователей.
Следует ли вам реализовать репозиторий, зависит от того, сколько пользователей у вас будет для вашей базы данных, насколько важно модульное тестирование и как часто вы ожидаете, что ваша база данных будет меняться.