[英]Filtering on Include in EF Core
I'm trying to filter on the initial query.我正在尝试过滤初始查询。 I have nested include leafs off a model.我已经嵌套了 model 的叶子。 I'm trying to filter based on a property on one of the includes.我正在尝试根据其中一个包含的属性进行过滤。 For example:例如:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
How can I also say .Where(w => w.post.Author == "me")
?我还能怎么说.Where(w => w.post.Author == "me")
?
Entity Framework core 5 is the first EF version to support filtered Include
. Entity Framework core 5 是第一个支持过滤Include
EF 版本。
Supported operations:支持的操作:
Where
OrderBy(Descending)/ThenBy(Descending)
Skip
Take
Some usage examples (from the original feature request and the github commmit ) :一些使用示例(来自原始功能请求和github 提交):
Only one filter allowed per navigation, so for cases where the same navigation needs to be included multiple times (eg multiple ThenInclude on the same navigation) apply the filter only once, or apply exactly the same filter for that navigation.每个导航只允许一个过滤器,因此对于需要多次包含相同导航的情况(例如,在同一导航上包含多个 ThenInclude)仅应用一次过滤器,或为该导航应用完全相同的过滤器。
context.Customers
.Include(c => c.Orders.Where(o => o.Name != "Foo")).ThenInclude(o => o.OrderDetails)
.Include(c => c.Orders).ThenInclude(o => o.Customer)
or或者
context.Customers
.Include(c => c.Orders.Where(o => o.Name != "Foo")).ThenInclude(o => o.OrderDetails)
.Include(c => c.Orders.Where(o => o.Name != "Foo")).ThenInclude(o => o.Customer)
Another important note:另一个重要说明:
Collections included using new filter operations are considered to be loaded.使用新过滤器操作包含的集合被视为已加载。
That means that if lazy loading is enabled, addressing one customer's Orders
collection from the last example won't trigger a reload of the entire Orders
collection.这意味着如果启用了延迟加载,则从上一个示例中解决一个客户的Orders
集合将不会触发整个Orders
集合的重新加载。
Also, two subsequent filtered Include
s in the same context will accumulate the results.此外,同一上下文中的两个后续过滤的Include
将累积结果。 For example...例如...
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...followed by... ...其次是...
context.Customers.Include(c => c.Orders.Where(o => o.IsDeleted))
...will result in customers
with Orders
collections containing all orders. ...将导致customers
的Orders
集合包含所有订单。
If other Order
s are loaded into the same context, more of them may get added to a customers.Orders
collection because of relationship fixup .如果其他Order
被加载到相同的上下文中,由于关系修复,更多的Order
可能会被添加到customers.Orders
集合中。 This is inevitable because of how EF's change tracker works.这是不可避免的,因为 EF 的变更跟踪器是如何工作的。
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...followed by... ...其次是...
context.Orders.Where(o => o.IsDeleted).Load();
...will again result in customers
with Orders
collections containing all orders. ...将再次导致customers
的Orders
集合包含所有订单。
The filter expression should contain predicates that can be used as a stand-alone predicate for the collection.过滤器表达式应包含可用作集合的独立谓词的谓词。 An example will make this clear.一个例子将说明这一点。 Suppose we want to include orders filtered by some property of Customer
:假设我们想要包含由Customer
的某些属性过滤的订单:
context.Customers.Include(c => c.Orders.Where(o => o.Classification == c.Classification))
It compiles, but it'll throw a very technical runtime exception, basically telling that o.Classification == c.Classification
can't be translated because c.Classification
can't be found.它编译,但它会抛出一个非常技术性的运行时异常,基本上告诉o.Classification == c.Classification
无法翻译,因为c.Classification
。 The query has to be rewritten using a back-reference from Order
to Customer
:必须使用从Order
到Customer
的反向引用重写查询:
context.Customers.Include(c => c.Orders.Where(o => o.Classification == o.Customer.Classification))
The predicate o => o.Classification == o.Customer.Classification)
is "stand alone" in the sense that it can be used to filter Orders
independently:谓词o => o.Classification == o.Customer.Classification)
是“独立的”,因为它可以用于独立过滤Orders
:
context.Orders.Where(o => o.Classification == o.Customer.Classification) // No one would try 'c.Classification' here
This restriction may change in later EF versions than the current stable version (EF core 5.0.7).此限制可能会在比当前稳定版本(EF 核心 5.0.7)更高的 EF 版本中更改。
Since Where
is an extension method on IEnumerable
it's clear that only collections can be filtered.由于Where
是IEnumerable
上的扩展方法,因此很明显只能过滤集合。 It's not possible to filter reference navigation properties.无法过滤参考导航属性。 If we want to get orders and only populate their Customer
property when the customer is active, we can't use Include
:如果我们想获得订单并且只在客户处于活动状态时填充他们的Customer
属性,我们不能使用Include
:
context.Orders.Include(c => c.Customer.Where( ... // obviously doesn't compile
Filtered Include
has given rise to some confusion on how it affects filtering a query as a whole. Filtered Include
在如何影响整个查询的过滤方面引起了一些混淆。 The rule of the thumb is: it doesn't.经验法则是:不会。
The statement...该声明...
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...returns all customers from the context, not only the ones with undeleted orders. ...从上下文中返回所有客户,而不仅仅是订单未删除的客户。 The filter in the Include
doesn't affect the number of items returned by the main query. Include
中的过滤器不会影响主查询返回的项目数。
On the other hand, the statement...另一方面,声明...
context.Customers
.Where(c => c.Orders.Any(o => !o.IsDeleted))
.Include(c => c.Orders)
...only returns customers having at least one undeleted order, but having all of their orders in the Orders
collections. ...只返回至少有一个未删除的订单,但所有订单都在Orders
集合中的客户。 The filter on the main query doesn't affect the orders per customer returned by Include
.主查询的过滤器不会影响Include
返回的每个客户的订单。
To get customers with undeleted orders and only loading their undeleted orders, both filters are required:要获取未删除订单的客户并仅加载他们未删除的订单,需要两个过滤器:
context.Customers
.Where(c => c.Orders.Any(o => !o.IsDeleted))
.Include(c => c.Orders.Where(o => !o.IsDeleted))
Another area of confusion is how filtered Include
and projections ( select new { ... }
) are related.另一个令人困惑的领域是过滤后的Include
和投影( select new { ... }
)是如何相关的。 The simple rule is: projections ignore Include
s, filtered or not.简单的规则是:投影忽略Include
s,过滤与否。 A query like...像这样的查询...
context.Customers
.Include(c => c.Orders)
.Select(c => new { c.Name, c.RegistrationDate })
...will generate SQL without a join to Orders
. ...将在没有连接到Orders
情况下生成 SQL。 As for EF, it's the same as...至于EF,它和...
context.Customers
.Select(c => new { c.Name, c.RegistrationDate })
It gets confusing when the Include
is filtered, but Orders
are also used in the projection:当Include
被过滤时,它会变得混乱,但Orders
也用于投影:
context.Customers
.Include(c => c.Orders.Where(o => !o.IsDeleted))
.Select(c => new
{
c.Name,
c.RegistrationDate,
OrderDates = c.Orders.Select(o => o.DateSent)
})
One might expect that OrderDates
only contains dates from undeleted orders, but they contain the dates from all Orders
.人们可能期望OrderDates
只包含来自未删除订单的日期,但它们包含来自所有Orders
的日期。 Again, the projection completely ignores the Include
.同样,投影完全忽略了Include
。 Projection and Include
are separate worlds.投影和Include
是不同的世界。
How strictly they lead their own lives is amusingly demonstrated by this query:这个问题有趣地证明了他们过着自己的生活是多么严格:
context.Customers
.Include(c => c.Orders.Where(o => !o.IsDeleted))
.Select(c => new
{
Customer = c,
OrderDates = c.Orders.Select(o => o.DateSent)
})
Now pause for a moment and predict the outcome...现在暂停片刻并预测结果......
The not so simple rule is: projections don't always ignore Include
.不那么简单的规则是:投影并不总是忽略Include
。 When there is an entity in the projection to which the Include
can be applied, it is applied.当投影中有一个实体可以应用Include
时,它就会被应用。 That means that Customer
in the projection contains its undeleted Orders
, whereas OrderDates
still contains all dates.这意味着投影中的Customer
包含其未删除的Orders
,而OrderDates
仍包含所有日期。 Did you get it right?你做对了吗?
Not doable.不可行。
There is an on-going discussion about this topic: https://github.com/aspnet/EntityFramework/issues/1833关于这个话题有一个正在进行的讨论: https : //github.com/aspnet/EntityFramework/issues/1833
I'd suggest to look around for any of the 3rd party libraries listed there, ex.: https://github.com/jbogard/EntityFramework.Filters我建议四处看看那里列出的任何 3rd 方库,例如: https : //github.com/jbogard/EntityFramework.Filters
You can also reverse the search.您也可以反向搜索。
{
var blogs = context.Author
.Include(author => author.posts)
.ThenInclude(posts => posts.blogs)
.Where(author => author == "me")
.Select(author => author.posts.blogs)
.ToList();
}
Not sure about Include() AND ThenInclude(), but it's simple to do that with a single include:不确定 Include() AND ThenInclude(),但使用单个 include 很容易做到这一点:
var filteredArticles =
context.NewsArticles.Include(x => x.NewsArticleRevisions)
.Where(article => article.NewsArticleRevisions
.Any(revision => revision.Title.Contains(filter)));
Hope this helps!希望这可以帮助!
Although it's (still in discussion) not doable with EF Core, I've managed to do it using Linq to Entities over EF Core DbSet.尽管(仍在讨论中)使用 EF Core 不可行,但我已经设法通过 EF Core DbSet 使用 Linq to Entities 来实现。 In your case instead of:在您的情况下,而不是:
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList()
.. you'll have: .. 你将拥有:
await (from blog in this.DbContext.Blogs
from bPost in blog.Posts
from bpAuthor in bPost.Author
where bpAuthor = "me"
select blog)
.ToListAsync();
I used below package Use Z.EntityFramework.Plus我使用了下面的包 Use Z.EntityFramework.Plus
IncludeFilter and IncludeFilterByPath two methods are which you can use.您可以使用 IncludeFilter 和 IncludeFilterByPath 两种方法。
var list = context.Blogs.IncludeFilter(x => x.Posts.Where(y => !y.IsSoftDeleted))
.IncludeFilter(x => x.Posts.Where(y => !y.IsSoftDeleted)
.SelectMany(y => y.Comments.Where(z => !z.IsSoftDeleted)))
.ToList();
Here is the example https://dotnetfiddle.net/SK934m这是示例https://dotnetfiddle.net/SK934m
Or you can do like this或者你可以这样做
GetContext(session).entity
.Include(c => c.innerEntity)
.Select(c => new Entity()
{
Name = c.Name,
Logo = c.Logo,
InnerEntity= c.InnerEntity.Where(s => condition).ToList()
})
Interesting case and it worked!!有趣的案例,它奏效了!!
If you have table/model user(int id, int? passwordId, ICollection<PwdHist> passwordHistoryCollection)
where collection is history of passwords.如果您有表/模型user(int id, int? passwordId, ICollection<PwdHist> passwordHistoryCollection)
,其中集合是密码历史记录。 Could be many or none.可能很多,也可能没有。
And PwdHistory(int id, int UserId, user User)
.和PwdHistory(int id, int UserId, user User)
。 This has a quasi relationship via attributes.这通过属性具有准关系。
Needed to get user
, with related current password record, while leaving historical records behind.需要获取user
,以及相关的当前密码记录,同时留下历史记录。
User user = _userTable
.Include(u => u.Tenant)
.Include(u => u.PwdHistory.Where(p => p.Id == p.PwdUser.PasswordId))
.Where(u => u.UserName == userName)
.FirstOrDefault();
Most interesting part is .Include(u => u.PwdHistory.Where(p => p.Id == p.PwdUser.PasswordId))
最有趣的部分是.Include(u => u.PwdHistory.Where(p => p.Id == p.PwdUser.PasswordId))
We can use by extension我们可以通过扩展使用
public static IQueryable<TEntity> IncludeCondition<TEntity, TProperty>(this IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> predicate, bool? condition) where TEntity : class where TProperty : class
{
return condition == true ? query.Include(predicate) : query;
}
Usage;用法;
_context.Tables.IncludeCondition(x => x.InnerTable, true)
This task can be accomplished with two queries.此任务可以通过两个查询来完成。 For example:例如:
var query = _context.Employees
.Where(x =>
x.Schedules.All(s =>
s.ScheduleDate.Month != DateTime.UtcNow.AddMonths(1).Month &&
s.ScheduleDate.Year != DateTime.UtcNow.AddMonths(1).Year) ||
(x.Schedules.Any(s =>
s.ScheduleDate.Month == DateTime.UtcNow.AddMonths(1).Month &&
s.ScheduleDate.Year == DateTime.UtcNow.AddMonths(1).Year) &&
x.Schedules.Any(i => !i.ScheduleDates.Any())));
var employees = await query.ToListAsync();
await query.Include(x => x.Schedules)
.ThenInclude(x => x.ScheduleDates)
.SelectMany(x => x.Schedules)
.Where(s => s.ScheduleDate.Month == DateTime.UtcNow.AddMonths(1).Month &&
s.ScheduleDate.Year == DateTime.UtcNow.AddMonths(1).Year).LoadAsync();
I didn't see the simple answer above -- not knocking the other answers.我没有看到上面的简单答案——没有敲其他答案。 The most popular is inclusive for just about any use case.最受欢迎的是几乎适用于任何用例。
C# 9, EF Core 6, was able to do this: C# 9,EF Core 6,能够做到这一点:
.Include(i => string.Join(", ", i.ApplicablePids.OrderBy(x =>x.CompareTo(i.ApplicablePids))))
I'm new to all this fluent LINQ stuff -- did I miss something here?我对所有这些流利的 LINQ 东西都很陌生——我在这里错过了什么吗?
The easiest inline solution to this I have used would be something like:我使用过的最简单的内联解决方案是这样的:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.FromSql("Select b.* from Blogs b inner join Posts p on b.BlogId = p.BlogId where p.Author = 'me'")
.ToList();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.