提问人:Jason N. Gaylord 提问时间:4/26/2017 最后编辑:Ian KempJason N. Gaylord 更新时间:9/23/2022 访问量:119012
筛选 EF Core 中的“包含”
Filtering on Include in EF Core
问:
我正在尝试对初始查询进行过滤。我已经嵌套了模型中的叶子。我正在尝试根据其中一个包含的属性进行过滤。例如:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
我怎么能说呢?.Where(w => w.post.Author == "me")
答:
不可行。
关于这个话题的讨论正在进行中:https://github.com/aspnet/EntityFramework/issues/1833
我建议四处寻找那里列出的任何第三方库,例如:https://github.com/jbogard/EntityFramework.Filters
评论
您也可以撤消搜索。
{
var blogs = context.Author
.Include(author => author.posts)
.ThenInclude(posts => posts.blogs)
.Where(author => author == "me")
.Select(author => author.posts.blogs)
.ToList();
}
评论
不确定 Include() 和 ThenInclude(),但使用单个 include 很简单:
var filteredArticles =
context.NewsArticles.Include(x => x.NewsArticleRevisions)
.Where(article => article.NewsArticleRevisions
.Any(revision => revision.Title.Contains(filter)));
希望这有帮助!
评论
尽管 EF Core 无法实现(仍在讨论中),但我已经设法使用 EF Core DbSet 上的 Linq to Entities 来做到这一点。在您的情况下,而不是:
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList()
..您将拥有:
await (from blog in this.DbContext.Blogs
from bPost in blog.Posts
from bpAuthor in bPost.Author
where bpAuthor = "me"
select blog)
.ToListAsync();
评论
Entity Framework core 5 是第一个支持筛选 Include
的 EF 版本。
运作方式
支持的操作:
Where
OrderBy(Descending)/ThenBy(Descending)
Skip
Take
每个导航只允许一个筛选器,因此,对于需要多次包含同一导航(例如,同一导航上的多个 ThenInclude)的情况,请仅应用一次筛选器,或为该导航应用完全相同的筛选器。
context.Customers
.Include(c => c.Orders.Where(o => o.Name != "Foo")).ThenInclude(o => o.OrderDetails)
.Include(c => c.Orders).ThenInclude(o => o.Customer)
或
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)
另一个重要提示:
使用新筛选器操作包含的集合被视为已加载。
这意味着,如果启用了延迟加载,则处理上一个示例中的一个客户的集合不会触发整个集合的重新加载。Orders
Orders
此外,在同一上下文中,两个后续过滤的 s 将累积结果。例如。。。Include
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...其次。。。
context.Customers.Include(c => c.Orders.Where(o => o.IsDeleted))
...将导致包含所有订单的集合。customers
Orders
筛选的包含和关系修复
如果将其他 s 加载到同一上下文中,则由于关系修复,可能会将更多 s 添加到集合中。这是不可避免的,因为 EF 的更改跟踪器的工作方式。Order
customers.Orders
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...其次。。。
context.Orders.Where(o => o.IsDeleted).Load();
...将再次导致包含所有订单的集合。customers
Orders
筛选表达式
筛选器表达式应包含可用作集合的独立谓词的谓词。一个例子可以清楚地说明这一点。假设我们想包含按以下属性过滤的订单:Customer
context.Customers.Include(c => c.Orders.Where(o => o.Classification == c.Classification))
它可以编译,但它会抛出一个非常技术性的运行时异常,基本上是说无法翻译,因为找不到。必须使用 from 的反向引用重写查询:o.Classification == c.Classification
c.Classification
Order
Customer
context.Customers.Include(c => c.Orders.Where(o => o.Classification == o.Customer.Classification))
从某种意义上说,谓词是“独立的”,因为它可以用于独立过滤:o => o.Classification == o.Customer.Classification)
Orders
context.Orders.Where(o => o.Classification == o.Customer.Classification) // No one would try 'c.Classification' here
此限制在比当前稳定版本 (EF core 5.0.7) 更高的 EF 版本中可能会更改。
什么可以(不能)过滤
由于是一个扩展方法,很明显只能过滤集合。无法筛选引用导航属性。如果我们想获取订单,并且只在客户处于活动状态时填充他们的属性,我们不能使用:Where
IEnumerable
Customer
Include
context.Orders.Include(o => o.Customer.Where( ... // obviously doesn't compile
筛选的包含与筛选查询
Filtered 在它如何影响整个查询的筛选方面引起了一些混淆。经验法则是:它没有。Include
声明...
context.Customers.Include(c => c.Orders.Where(o => !o.IsDeleted))
...从上下文中返回所有客户,而不仅仅是具有未删除订单的客户。中的筛选器不会影响主查询返回的项数。Include
另一方面,声明...
context.Customers
.Where(c => c.Orders.Any(o => !o.IsDeleted))
.Include(c => c.Orders)
...仅返回至少有一个未删除订单的客户,但其所有订单都在集合中。主查询的筛选器不会影响 返回的每位客户的订单数。Orders
Include
要获取具有未删除订单且仅加载其未删除订单的客户,这两个过滤器都是必需的:
context.Customers
.Where(c => c.Orders.Any(o => !o.IsDeleted))
.Include(c => c.Orders.Where(o => !o.IsDeleted))
筛选的包含和投影
另一个令人困惑的领域是过滤和投影 () 之间的关系。简单的规则是:投影忽略 s,无论是否过滤。像这样的查询...Include
select new { ... }
Include
context.Customers
.Include(c => c.Orders)
.Select(c => new { c.Name, c.RegistrationDate })
...将生成 SQL 而不加入 。至于EF,它和...Orders
context.Customers
.Select(c => new { c.Name, c.RegistrationDate })
当 被过滤时,它会变得令人困惑,但在投影中也会使用: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)
})
人们可能认为它只包含未删除订单的日期,但它们包含所有 .同样,投影完全忽略了 .投影和是独立的世界。OrderDates
Orders
Include
Include
他们过着多么严格的生活,这个问题很有趣地证明了这一点:
context.Customers
.Include(c => c.Orders.Where(o => !o.IsDeleted))
.Select(c => new
{
Customer = c,
OrderDates = c.Orders.Select(o => o.DateSent)
})
现在暂停片刻,预测结果......
不那么简单的规则是:预测并不总是忽略。当投影中存在可以应用的图元时,将应用该图元。这意味着在投影中包含其未删除的 ,而仍然包含所有日期。你做对了吗?Include
Include
Customer
Orders
OrderDates
评论
new { root.Property1, Children = root.ChildCollection.Where(...).Select(c => new { c.ChildProperty1, ... })
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager
Include
.Select(c => c.Orders)
....FirstOrDefault()
orders = c.Orders
可以通过两个查询完成此任务。例如:
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();
评论
SelectMany
Includes
ScheduleDates
Schedules
SelectMany
Include
我使用了下面的包 使用 Z.EntityFramework.Plus
可以使用 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();
下面是示例 https://dotnetfiddle.net/SK934m
或者你可以这样做
GetContext(session).entity
.Include(c => c.innerEntity)
.Select(c => new Entity()
{
Name = c.Name,
Logo = c.Logo,
InnerEntity= c.InnerEntity.Where(s => condition).ToList()
})
评论
Include
Include
有趣的案例,它奏效了!
如果您有表/模型,其中收集是密码历史记录。可以是多个,也可以是没有。user(int id, int? passwordId, ICollection<PwdHist> passwordHistoryCollection)
和。这通过属性具有准关系。PwdHistory(int id, int UserId, user User)
需要获取,并带有相关的当前密码记录,同时留下历史记录。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();
最有趣的部分是.Include(u => u.PwdHistory.Where(p => p.Id == p.PwdUser.PasswordId))
- 使用用户和许多密码
- 使用用户且无密码
- 无需用户即可使用
我们可以通过扩展使用
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;
}
用法;
_context.Tables.IncludeCondition(x => x.InnerTable, true)
评论