Подтвердить что ты не робот

Как фильтровать вложенные объекты Entity Framework коллекции?

Вот проблема: Мне нужно вернуть коллекцию объектов с фильтрованными вложенными коллекциями. Например: есть магазин с заказами, и мне нужно вернуть коллекцию с магазинами, в которые входят вложенные коллекции с заказами, но без заказов от клиентов, отмеченных как удаленные.

Вот что я пытаюсь сделать. Но до сих пор не повезло. Любые предложения оцениваются:)

public List<StoreEntity> GetStores(Func<Store, bool> storeFilter, Predicate<OrderEntity> orderFileter)
{
    IQueryable<StoreEntity> storeEntities = Context.Stores
        .Include(o => o.Order)
        .Include(cu => cu.Orders.Select(c => c.Customer))
        .Where(storeFilter)
        //.Where(rcu=>rcu.Orders.Select(cu=>cu.Customer.Deleted==false)) //just test this doesn't work
        .AsQueryable();

    List<StoreEntity> storeEntities = storeEntities.ToList();

    //storeEntities.ForEach(s => s.Orders.ToList().RemoveAll(c=>c.Customer.Deleted==true)); // doesn't work

    foreach (StoreEntity storeEntity in storeEntities)
    {
        storeEntity.Orders.ToList().RemoveAll(r=>r.Customer.Deleted==true);
    }

    return storeEntities;
}

Проблема в том, что фильтр не применяется. Клиенты, которые удалили флаг, установлены как истинное пребывание в коллекции.

4b9b3361

Ответ 1

Вы не можете сделать это прямо "аккуратным" способом, но у вас есть несколько вариантов.
Прежде всего, вы можете явно загружать дочернюю коллекцию после того, как вы извлекли магазины. См. Применение фильтров при явной загрузке связанных объектов.

Если вы не хотите делать дополнительные поездки в базу данных, вам придется создать свой собственный запрос и спроектировать родительскую коллекцию и отфильтрованные дочерние коллекции на другой объект вручную. Примеры см. на следующих примерах:
Linq To Entities - как фильтровать дочерние объекты
Запрос LINQ - как сортировать и фильтровать при желаемой выборке

Edit

Кстати, ваша первая попытка .Where(rcu=>rcu.Orders.Select(cu=>cu.Customer.Deleted==false)) не работает, так как вы применяете фильтр к своей родительской коллекции (хранилищам), а не к вложенной коллекции (например, ко всем магазинам, которые не удалили клиентов).
Логически, код, фильтрующий вложенную коллекцию, должен быть помещен в метод Include. В настоящее время Include поддерживает только оператор Select, но лично я считаю, что время для команды EF реализовать что-то вроде:

.Include(cu => cu.Orders.Select(c => c.Customers.Where(cust => !cust.IsDeleted)));

Ответ 2

Проблема с кодом, который у вас есть в данный момент:

storeEntity.Orders.ToList().RemoveAll(r=>r.Customer.Deleted==true);

storeEntity.Orders.ToList() возвращает новый List<OrderEntity> с содержимым storeEntity.Orders. Из этого нового списка вы удаляете всех удаленных клиентов. Однако этот список не используется нигде после этого.

Однако, даже если он будет делать то, что вы хотите, это также удалит этих клиентов из базы данных, потому что ваши объекты StoreEntity все еще подключены к контексту данных!

Вы действительно хотите использовать фильтр, как вы впервые пробовали в прокомментированном Where. Пожалуйста, обратитесь к Yakimych за помощью.

Ответ 3

Старая тема, но я столкнулся с довольно похожими проблемами. Я много искал, и ссылка MSDN, предоставленная Якимычем, наконец намекнула мне на решение: явно отключить ленивую загрузку, а затем делать запросы для фильтрации свойств навигации. Затем результат будет "привязан" к основному запросу, который даст что-то вроде этого:

Context.Configuration.LazyLoadingEnabled = false;

var filteredOrders = Context.Orders.Where(x => x.Customer.Delete == false);

IQueryable<StoreEntity> storeEntities = Context.Stores
.Include(o => o.Order)
.Include(cu => cu.Orders.Select(c => c.Customer))
.Where(storeFilter)
.AsQueryable();