简体   繁体   English

Entity Framework Core 2.2 orderby在本地评估

[英]Entity Framework Core 2.2 orderby evaluated locally

I have this semi-complex query that counts the most voted user post of the last 7 days: 我有这个半复杂的查询,该查询计算最近7天获得投票最多的用户帖子:

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();

What I try to do here is to group user votes by PostId , sum the evaluations of their votes by field Sign (can be -1, 0 or 1), then join it with some additional data like game Id/Title and post texts, filter non-public or too old posts, then order it by rank and then by create date, then map it onto DTO and return the very first result if present. 我在这里尝试做的是将用户投票按PostId ,通过按字段Sign (可以是-1、0或1)对用户投票的评估求和,然后将其与一些其他数据(例如游戏ID /标题和发布文本)结合起来,过滤非公开或过旧的帖子,然后按等级排序,然后按创建日期排序,然后将其映射到DTO并返回第一个结果(如果有)。

All the fields here are simple basic types: the Vote.Sign is int , Post.CreateDate is DateTime , all the *Id are Guid and Text/Title/Commentary are string . 这里的所有字段都是简单的基本类型: Vote.SignintPost.CreateDateDateTime ,所有*IdGuidText/Title/Commentarystring

I get the warning: 我得到警告:

warn: Microsoft.EntityFrameworkCore.Query[20500] 警告:Microsoft.EntityFrameworkCore.Query [20500]
The LINQ expression 'orderby [bestPost].TotalRating desc' could not be translated and will be evaluated locally. LINQ表达式'orderby [bestPost] .TotalRating desc'无法翻译,将在本地进行评估。
warn: Microsoft.EntityFrameworkCore.Query[20500] 警告:Microsoft.EntityFrameworkCore.Query [20500]
The LINQ expression 'FirstOrDefault()' could not be translated and will be evaluated locally. LINQ表达式“ FirstOrDefault()”无法翻译,将在本地进行评估。

If I remove the sort by TotalRating and only leave the CreateDate sorting, it works fine, creates proper LIMIT request. 如果我通过TotalRating删除排序,而只保留CreateDate排序,则它可以正常工作,并创建适当的LIMIT请求。 But with TotalRating the query looks like this: 但是使用TotalRating ,查询看起来像这样:

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)

And it looks pretty bad to be calculated in dotnet runtime. 在dotnet运行时中进行计算似乎很糟糕。

I tried to wrap the result in another select from , but it didn't help. 我尝试将结果包装到的另一个select from ,但没有帮助。 I also cannot do the group by on all the columns because then I won't be able to aggregate things like ModuleId because EF Core 2.2 does not support the group.FirstOrDefault things and PostgreSQL does not support max(uuid) (otherwise I could use group.Max(g => g.ModuleId) ). 我也不能对所有列进行分组,因为那样我将无法聚合ModuleId类的ModuleId因为EF Core 2.2不支持该组group.FirstOrDefault和PostgreSQL不支持max(uuid) (否则我可以使用group.Max(g => g.ModuleId) )。

What am I doing wrong? 我究竟做错了什么?

What happens if you combine the gist of votedPosts into the query so you don't duplicate the join on posts ? 如果你结合的要点,会发生什么votedPosts到查询,所以你不要重复joinposts

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM