トピックと投稿のリストを含むフォーラムをイメージしてみましょう。各トピックのトピックのリストと最後の投稿のタイトル(日付順)を取得したい。
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(トピックごとに個別のクエリを実行)を使用してそれを実行できることはわかっていますが、それは避けたいです。
基本的に私が今やっていることは実行した後です
_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();
_
これは、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つのクエリ(Nではなく)になります。
非常に効率的ではありませんが、ページあたりのトピックの数が非常に少ないため、効果的です。
EF Core 2.1では、GroupBy LINQ演算子は、ほとんどの場合、SQL GROUP BY句への変換のみをサポートします。 sum、maxなどの集計関数...
EF Coreで完全なサポートグループまで使用できます Dapper