Как отфильтровать вложенные объекты коллекции Entity Framework? - PullRequest
22 голосов
/ 16 августа 2011

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

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

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;
}

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

Ответы [ 3 ]

31 голосов
/ 16 августа 2011

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

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

Редактировать

Кстати, ваша первая попытка .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)));
3 голосов
/ 16 августа 2011

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

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

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

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

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

0 голосов
/ 09 октября 2013

Старая тема, но я столкнулся с довольно похожей проблемой. Я много искал, и ссылка на 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...