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 в некоторых сценариях:

  1. Если вы используете EF вместе с функцией отложенной загрузки, выборка вашего объекта приносит все связанные коллекции, которые были отмечены как виртуальные в вашем классе. Таким образом, делая context.User.Single( x => x.id == USER_ID ); вы получите все пользовательские адреса, связанные с пользователем, если вы не отключите отложенную загрузку для своей коллекции, удалив ключевое слово Virtual из свойства в классе User.

  2. Если вы добавляете/удаляете коллекцию UserAddresses в своей программе и вызываете context.SaveChanges(); без удаления вашего контекста, в следующий раз, когда вы загрузите объект User, коллекция UserAddresses будет загружена из кеша контекста EF, а не из БД (ваши последние изменения). В этом случае вам нужно избавиться от контекста и создать новый контекст, прежде чем вывести пользователя из контекста. Например, если у вас есть пользователь с 5 элементами в коллекции UserAddresses, и вы отключаете один из элементов (item.Enabled = false), а затем вызываете context.SaveChanges() без удаления вашего контекста, в следующий раз, когда вы получите объект User из того же контекста он уже имеет 5 элементов в своей коллекции, которая поступает из кеша контекста и игнорирует ваш метод Load().

PS:

Функция отложенной загрузки включена, если применяются все нижеуказанные условия:

  1. context.Configuration.LazyLoadingEnabled = true;
  2. context.Configuration.ProxyCreationEnabled = true;
  3. UserAddresses был определен виртуальный в вашем классе пользователя.