如何过滤嵌套集合实体框架对象?

Posted

技术标签:

【中文标题】如何过滤嵌套集合实体框架对象?【英文标题】:How to filter nested collection Entity Framework objects? 【发布时间】:2011-10-28 02:33:56 【问题描述】:

问题来了: 我需要返回带有过滤嵌套集合的对象集合。 例如:有一个带有订单的商店,我需要返回一个包含商店的集合,其中包含带有订单的嵌套集合,但没有来自标记为已删除的客户的订单。

这是我尝试做的。但仍然没有运气。任何建议表示赞赏:)

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 的客户保留在集合中。

【问题讨论】:

还有什么问题?它不会编译吗?它会引发运行时异常吗?它会运行但返回错误的数据吗? 我最终使用了这个 nuget 包:Z.EntityFramework.Plus.QueryIncludeFilter.EF6 此处的文档:github.com/zzzprojects/EntityFramework-Plus/wiki/… @BronDavies 这个 Nuget 包应该是公认的答案,而不是评论! 【参考方案1】:

您不能直接以“简洁”的方式执行此操作,但您有几个选择。 首先,您可以在获取商店后显式加载子集合。请参阅Applying filters when explicitly loading related entities 部分。

如果您不想额外访问数据库,则必须构建自己的查询并将父集合和过滤后的子集合手动投影到另一个对象上。有关示例,请参阅以下问题:Linq To Entities - how to filter on child entitiesLINQ Query - how sort and filter on eager fetch

编辑

顺便说一句,您的第一次.Where(rcu=&gt;rcu.Orders.Select(cu=&gt;cu.Customer.Deleted==false)) 尝试不起作用,因为这样您将过滤器应用于您的父集合(商店)而不是嵌套集合(例如,所有没有删除客户的商店) . 从逻辑上讲,过滤嵌套集合的代码应该放在Include 方法中。目前,Include 只支持Select 语句,但我个人认为是时候让 EF 团队实现类似的东西了:

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

【讨论】:

是的,我同意。 (我知道那东西不起作用,但仍然想尝试一下,谁知道这个功能可能被隐藏了,我会感到惊喜),是的,这种包含会很高兴。我决定使用自定义投影...不太喜欢这种方法,因为我必须分配很多愚蠢的值,例如( ID= r.ID、Name= r.Name ... 等等)。但至少它有效:) Thx 我同意 EF 团队应该实施过滤包含。为它投票here! 如果我有一个名为GetWithInclude() public IQueryable&lt;TEntity&gt; GetWithInclude(params Expression&lt;Func&lt;TEntity, object&gt;&gt;[] includeProperties) return includeProperties.Aggregate&lt;Expression&lt;Func&lt;TEntity, object&gt;&gt;, IQueryable&lt;TEntity&gt;&gt;(DbSet, (current, includeProperty) =&gt; current.Include(includeProperty)); 的通用方法怎么办?然而,我没有集合,我需要从投影中过滤。换句话说,我有一个需要相等的属性。【参考方案2】:

你目前的代码的问题是这一行:

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

storeEntity.Orders.ToList() 返回一个 new List&lt;OrderEntity&gt;,其内容为storeEntity.Orders。从这个新列表中,您删除了所有已删除的客户。但是,此后该列表不再使用。

但是,即使它会做你想做的事,这也会从数据库中删除这些客户,因为你的 StoreEntity 对象仍然连接到数据上下文!

您确实想使用您在评论Where 中首次尝试的过滤器。请参阅 Yakimych 的回答以获得帮助。

【讨论】:

【参考方案3】:

老话题,但我遇到了一个相当相似的问题。我搜索了很多,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();

【讨论】:

什么是 storeFilter ?

以上是关于如何过滤嵌套集合实体框架对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用谓词过滤子实体集合?

实体框架和自引用集合

实体框架代码首先用于具有属性集合的对象

按嵌套集合过滤计数排序

Hibernate:如何让两个集合字段指向相同的对象

实体框架与集合对象,使用 Automapper,更新时异常