LINQ To Entities Include + Where Method
У меня есть таблица NxN, представьте себе:
Пользователь (id,...) < - UserAddresses (id, userId, addressId, enabled,...) → Адреса (id,...)
UserAddresses содержит FK для пользователя и для адресации.
Для того, что я знаю, Entity, созданный пользователем Entity Framework, содержит коллекцию для UserAddresses. Адрес содержит коллекцию для UserAddresses, а определенный UserAddress содержит один refenrece для пользователя и для одного адреса.
Теперь я хочу сделать следующий запрос linq.
Для определенного идентификатора пользователя получите только userAddresses с включенным флагом, установленным в true.
Для определенного идентификатора пользователя userAddresses может содержать несколько записей, но для этого конкретного пользователя настроен только один.
Я могу выполнить запрос:
context.User.Include( x => x.UserAddresses )
.Include( x => x.UserAddresses.Select(y => y.Address) )
.Single( x => x.id == USER_ID )
но я действительно хочу, чтобы не загружать все UserAddresses для этого пользователя... Только тот, который содержит включен, настроен на TRUE!
Кто-нибудь может помочь мне сделать этот запрос?
Ответы
Ответ 1
В EF нет возможности частично загрузить свойство ассоциации. Попробуйте выбрать анонимный тип, чтобы взять только то, что вам нужно:
var result = context.User
.Where(u => u.Id == userId)
.Select(u => new {
Addresses = u.UserAddresses.Select(ua => ua.Address)
.Where(a => a.Enabled),
User = u // if you need this as well
})
.Single();
Это не будет загружать result.User.UserAddresses, но result.Addresses будет иметь именно то, что вы хотите.
Если вы действительно хотите вернуть все как часть класса User, вам нужно будет отключить result.User, а затем обновить result.User.UserAddresses, чтобы указать на результат. Адреса.
Ответ 2
Другой альтернативный вариант - использовать Load()
вместо Include()
:
var foundUser = context.User.Single(x => x.Id == USER_ID);
context.Entry(foundUser).Collection(u =>
u.UserAddresses).Query().Where(userAddress =>
userAddress.Enabled).Load();
Имейте в виду, что метод Load()
может игнорироваться EF в некоторых сценариях:
-
Если вы используете EF вместе с функцией отложенной загрузки, выборка вашего объекта приносит все связанные коллекции, которые были отмечены как виртуальные в вашем классе. Таким образом, делая context.User.Single( x => x.id == USER_ID );
вы получите все пользовательские адреса, связанные с пользователем, если вы не отключите отложенную загрузку для своей коллекции, удалив ключевое слово Virtual
из свойства в классе User.
-
Если вы добавляете/удаляете коллекцию UserAddresses в своей программе и вызываете context.SaveChanges(); без удаления вашего контекста, в следующий раз, когда вы загрузите объект User, коллекция UserAddresses будет загружена из кеша контекста EF, а не из БД (ваши последние изменения). В этом случае вам нужно избавиться от контекста и создать новый контекст, прежде чем вывести пользователя из контекста. Например, если у вас есть пользователь с 5 элементами в коллекции UserAddresses, и вы отключаете один из элементов (item.Enabled = false
), а затем вызываете context.SaveChanges()
без удаления вашего контекста, в следующий раз, когда вы получите объект User из того же контекста он уже имеет 5 элементов в своей коллекции, которая поступает из кеша контекста и игнорирует ваш метод Load()
.
PS:
Функция отложенной загрузки включена, если применяются все нижеуказанные условия:
- context.Configuration.LazyLoadingEnabled = true;
- context.Configuration.ProxyCreationEnabled = true;
- UserAddresses был определен виртуальный в вашем классе пользователя.