让我们想象一个论坛,其中包含主题和帖子列表。 我想获取主题列表以及每个主题的最新帖子标题(按日期)。
有没有一种方法可以使用EF Core(2.1)? 在SQL中,可以像
SELECT Posts.Title, Posts.CreatedDate, Posts.TopicId FROM
(SELECT Max(CreatedDate), TopicId FROM Posts GROUP BY TopicId) lastPosts
JOIN Posts ON Posts.CreatedDate = lastPosts.CreatedDate AND Posts.TopicId = lastPosts.TopicId
在EFCore中,我可以选择LastDates
_context.Posts.GroupBy(x => x.TopicId, (x, y) => new
{
CreatedDate = y.Max(z => z.CreatedDate),
TopicId = x,
});
如果我运行.ToList(),查询将正确翻译为GROUP BY
。
但是我不能走的更远。
以下内容在内存中执行,而不是在SQL中执行(导致SELECT * FROM Posts
):
.GroupBy(...)
.Select(x => new
{
x.TopicId,
Post = x.Posts.Where(z => z.CreatedDate == x.CreatedDate)
//Post = x.Posts.FirstOrDefault(z => z.CreatedDate == x.CreatedDate)
})
尝试加入JOIN会导致NotSupportedException(无法解析表达式):
.GroupBy(...)
.Join(_context.Posts,
(x, y) => x.TopicId == y.TopicId && x.CreatedDate == y.CreatedDate,
(x, post) => new
{
post.Title,
post.CreatedDate,
})
我知道我可以使用SELECT N + 1(针对每个主题运行一个单独的查询)来做到这一点,但我想避免这种情况。
答案 0 :(得分:5)
基本上我现在要做的就是跑步后
var topics = _context.Posts.GroupBy(x => x.TopicId, (x, y) => new
{
CreatedDate = y.Max(z => z.CreatedDate),
TopicId = x,
}).ToList();
我建立以下查询:
Expression<Func<Post, bool>> lastPostsQuery = post => false;
foreach (var topic in topics)
{
lastPostsQuery = lastPostsQuery.Or(post => post.TopicId == topic.TopicId && post.CreatedDate = topic.CreatedDate); //.Or is implemented in PredicateBuilder
}
var lastPosts = _context.Posts.Where(lastPostsQuery).ToList();
哪个查询(而不是N个查询)像SELECT * FROM Posts WHERE (Posts.TopicId == 1 AND Posts.CreatedDate = '2017-08-01') OR (Posts.TopicId == 2 AND Posts.CreatedDate = '2017-08-02') OR ...
。
效率不是很高,但是由于每页的主题数量很少,因此可以解决问题。
答案 1 :(得分:4)
我不知道从哪个版本的EFCore开始,但是现在有一个更简单的单查询替代方法:
context.Topic
.SelectMany(topic => topic.Posts.OrderByDescending(z => z.CreatedDate).Take(1),
(topic, post) => new {topic.Id, topic.Title, post.Text, post.CreatedDate})
.OrderByDescending(x => x.CreatedDate)
.ToList();
答案 2 :(得分:1)
在EF Core 2.1中,GroupBy LINQ运算符仅在大多数常见情况下支持转换为SQL GROUP BY子句。汇总函数,例如sum,max ...
您可以在EF Core全面支持之前使用Dapper