如何过滤嵌套的集合Entity Framework对象?

25

这里是问题: 我需要返回一个包含已筛选嵌套集合对象的集合。 例如:有一个商店,有订单,我需要返回包含有订单的商店集合,但不包括被标记为删除的顾客订单。

这是我尝试做的事情。但是还没有成功。欢迎提出建议 :)

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

问题在于筛选器没有起作用。将删除标志设置为True的客户仍然留在集合中。

问题是什么?它不能编译吗?它抛出了运行时异常吗?它可以运行,但返回错误的数据吗? - Daniel Hilgarth
2
我最终使用了这个NuGet包:Z.EntityFramework.Plus.QueryIncludeFilter.EF6,文档在这里:https://github.com/zzzprojects/EntityFramework-Plus/wiki/EF-Query-IncludeFilter-%7C-Entity-Framework-Include-Related-Entities-using-Where-Filter - Bron Davies
@BronDavies 这个Nuget包应该是被接受的答案,而不是一个评论! - Avrohom Yisroel
3个回答

35
你无法以“简洁”的方式直接实现这一点,但你有几个选项。
首先,你可以在获取店铺之后显式地加载子集合。请参见“显式加载相关实体时应用筛选器”部分。
如果你不想额外访问数据库,你需要构建自己的查询,手动将父集合和经过筛选的子集合投影到另一个对象上。请参见以下问题中的示例:
Linq To Entities - how to filter on child entities
LINQ Query - how sort and filter on eager fetch 编辑 顺便说一下,你的第一个.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)));

1
是的,我同意。(我知道那个东西不起作用,但还是想试一试,谁知道也许那个功能被隐藏了,我会惊喜的),而且,这种包含方式很好。我决定使用自定义投影...不太喜欢这种方法,因为我不得不分配很多愚蠢的值,比如(ID=r.ID,Name=r.Name ...等等)。但至少它能工作:) 谢谢 - Naz
7
我同意EF团队应该实现过滤的Include。请在此处为它投票here - Chris
如果我有一个名为GetWithInclude()的通用方法public IQueryable GetWithInclude(params Expression>[] includeProperties) { return includeProperties.Aggregate>, IQueryable>(DbSet, (current, includeProperty) => current.Include(includeProperty)); }但是,我没有集合,需要从投影中进行筛选。换句话说,我有一个需要等于某个值的属性。 - Vyache

3
您目前的代码存在问题,问题就在于这一行:
storeEntity.Orders.ToList().RemoveAll(r=>r.Customer.Deleted==true);
storeEntity.Orders.ToList() 返回一个List<OrderEntity>,其中包含 storeEntity.Orders 的内容。从这个新列表中,您可以删除所有被删除的客户。但是,在此之后,该列表没有被使用过。
然而,即使它能够做到您想要的事情,这也会从数据库中删除那些客户,因为您的 StoreEntity 对象仍连接到数据上下文!
您真正想要使用过滤器,就像您在注释中尝试的那样的Where。请参考Yakimych的答案来获得帮助。

-7

虽然这是一个老话题,但我遇到了一个非常相似的问题。我进行了大量搜索,最终 Yakimych 提供的 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();

1
storeFilter 是什么? - Kaushik Thanki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接