Ho questa query semi-complessa che conta il post dell'utente più votato degli ultimi 7 giorni:
var fromDate = dateTimeService.Now.Add(-interval);
var votedPosts =
from vote in dbContext.Votes
join post in dbContext.Posts on vote.PostId equals post.PostId
group new {vote.Sign, post.PostId} by post.PostId
into postVotes
select new {
PostId = postVotes.Key,
TotalRating = postVotes.Sum(pv => pv.Sign)
};
var bestPost = (
from post in dbContext.Posts
join votedPost in votedPosts on post.PostId equals votedPost.PostId
join room in dbContext.Rooms on post.RoomId equals room.RoomId
join game in dbContext.Modules on room.ModuleId equals game.ModuleId
where room.RoomAccess == RoomAccessType.Open && post.CreateDate > fromDate
orderby votedPost.TotalRating descending,
post.CreateDate descending
select new BestPost
{
UserId = post.UserId,
ModuleId = game.ModuleId,
ModuleTitle = game.Title,
PostId = post.PostId,
PostText = post.Text,
PostCommentary = post.Commentary,
PostCreateDate = post.CreateDate,
TotalRating = bestPost.TotalRating
}).FirstOrDefault();
Quello che cerco di fare qui è raggruppare i voti degli utenti per PostId
, sommare le valutazioni dei loro voti per campo Sign
(può essere -1, 0 o 1), quindi aggiungerlo con alcuni dati aggiuntivi come Id / Title e post di testo, filtrare i messaggi non pubblici o troppo vecchi, quindi ordinarli per rank e quindi creare la data, quindi mapparlo su DTO e restituire il primo risultato se presente.
Tutti i campi qui sono semplici tipi base: il Vote.Sign
è int
, Post.CreateDate
è DateTime
, tutti gli *Id
sono Guid
e Text/Title/Commentary
sono string
.
Ottengo l'avvertimento:
warn: Microsoft.EntityFrameworkCore.Query [20500]
L'espressione LINQ 'orderby [bestPost] .TotalRating desc' non può essere tradotta e verrà valutata localmente.
warn: Microsoft.EntityFrameworkCore.Query [20500]
L'espressione LINQ 'FirstOrDefault ()' non può essere tradotta e verrà valutata localmente.
Se rimuovo l'ordinamento da TotalRating
e lascio solo l'ordinamento CreateDate
, funziona correttamente, crea la richiesta LIMIT
appropriata. Ma con TotalRating
la query si presenta così:
SELECT
t."PostId", t."TotalRating", post."CreateDate" AS "PostCreateDate",
post."UserId", game."ModuleId", game."Title" AS "ModuleTitle",
post."PostId" AS "PostId0", post."Text" AS "PostText",
post."Commentary" AS "PostCommentary"
FROM
"Posts" AS post
INNER JOIN
(SELECT
post0."PostId", SUM(vote."Sign")::INT AS "TotalRating"
FROM
"Votes" AS vote
INNER JOIN
"Posts" AS post0 ON vote."PostId" = post0."PostId"
GROUP BY
post0."PostId") AS t ON post."PostId" = t."PostId"
INNER JOIN
"Rooms" AS room ON post."RoomId" = room."RoomId"
INNER JOIN
"Modules" AS game ON room."ModuleId" = game."ModuleId"
WHERE
(room."RoomAccess" = 0) AND (post."CreateDate" > @__fromDate_0)
E sembra piuttosto male essere calcolato in runtime dotnet.
Ho provato a racchiudere il risultato in un'altra select from
, ma non è stato d'aiuto. Non riesco a fare il gruppo su tutte le colonne perché non potrò aggregare cose come ModuleId
perché EF Core 2.2 non supporta il group.FirstOrDefault
e PostgreSQL non supporta max(uuid)
(altrimenti potrei usare group.Max(g => g.ModuleId)
).
Che cosa sto facendo di sbagliato?
Cosa succede se votedPosts
la votedPosts
di votedPosts
alla query in modo da non duplicare il join
sui posts
?
var bestPost = (
from post in dbContext.Posts
join vote in dbContext.Votes on post.PostId equals vote.PostId into votej
let voteTotalRating = votej.Sum(pv => pv.Sign)
join room in dbContext.Rooms on post.RoomId equals room.RoomId
join game in dbContext.Modules on room.ModuleId equals game.ModuleId
where room.RoomAccess == RoomAccessType.Open && post.CreateDate > fromDate
orderby voteTotalRating descending,
post.CreateDate descending
select new BestPost {
UserId = post.UserId,
ModuleId = game.ModuleId,
ModuleTitle = game.Title,
PostId = post.PostId,
PostText = post.Text,
PostCommentary = post.Commentary,
PostCreateDate = post.CreateDate,
TotalRating = voteTotalRating
}).FirstOrDefault();