使用 EF Core 将 SQL 转换为 Linq

Convert SQL to Linq with EF Core

我正在使用 .NET Core 2.2、EF Core、C# 和 SQL Server 2017。 我无法将我需要的查询翻译成 Linq。

这是我需要转换的查询:

SELECT      TOP 5
            p.Id, 
            p.Title, 
            AVG(q.RatingValue) AvgRating
FROM        Movies AS p
INNER JOIN  Ratings AS q ON p.Id = q.MovieId
GROUP BY    p.Id, p.Title
ORDER BY    AvgRating DESC, p.Title ASC

前面查询的思路是根据Avg rating得到Top 5的电影,按照平均分最高的在前排序,如果平均分相同则按字母顺序排列。

到目前为止,这是我进行连接的查询,但仍然缺少:分组依据、平均值和排序:

public class MovieRepository : IMovieRepository
{
    private readonly MovieDbContext _moviesDbContext;
    public MovieRepository(MovieDbContext moviesDbContext)
    {
        _moviesDbContext = moviesDbContext;
    }

    public IEnumerable<Movie> GetTopFive()
    {
        var result = _moviesDbContext.Movies.OrderByDescending(x => x.Id).Take(5).
                     Include(x => x.Ratings);

        return result;
    }
}

这些是实体:

 public class Movie
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int YearOfRelease { get; set; }
    public string Genre { get; set; }
    public int RunningTime { get; set; }
    public IList<Rating> Ratings { get; set; }
}

public class Rating
{
    public int Id { get; set; }
    public int MovieId { get; set; }
    public int UserId { get; set; }
    public decimal RatingValue { get; set; }
}

我也尝试使用 Linqer 工具将我的查询转换为 Linq,但它不起作用。

对于将查询转换为 LINQ 方法 "GetTopFive" 的任何帮助,我将不胜感激。

谢谢

尝试如下:

public IEnumerable<Movie> GetTopFive()
{
    var result = _moviesDbContext.Ratings.GroupBy(r => r.MovieId).Select(group => new
        {
            MovieId = group.Key,
            MovieTitle = group.Select(g => g.Movie.Title).FirstOrDefault(),
            AvgRating = group.Average(g => g.RatingValue)
        }).OrderByDescending(s => s.AvgRating).Take(5).ToList();
    return result;
}

这将排除没有评级的电影。

但是如果你这样做(如artista_14的回答):

public IEnumerable<Movie> GetTopFive()
{
    var result = _moviesDbContext.Movies.GroupBy(x => new { x.Id, x.Title })
        .Select(x => new {
            Id = x.Key.Id,
            Title = x.Key.Title,
            Average = x.Average(y => y.Ratings.Sum(z => z.RatingValue))
    }).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();
    return result;
}

这也包括没有评级的电影。

注意:我看到您的 Rating 模型 class 不包含任何 Movie 导航 属性。请添加如下:

public class Rating
{
    public int Id { get; set; }
    public int MovieId { get; set; }
    public int UserId { get; set; }
    public decimal RatingValue { get; set; }

    public Movie Movie { get; set; }
}

试试这个 -

var data = _moviesDbContext.Movies.Include(x => x.Ratings)
            .Select(x => new {
                Id = x.Id,
                Title = x.Title,
                Average = (int?)x.Ratings.Average(y => y.RatingValue)
        }).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();

最后,这是运行良好的代码:

var data = _moviesDbContext.Movies.Include(x => x.Ratings)
            .Select(x => new MovieRating
            {
                Id = x.Id,
                Title = x.Title,
                Average = x.Ratings.Average(y => y.RatingValue)
            }).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();

        return data;

问题是在 select 中创建了一个匿名类型,所以这一行解决了这个问题:.Select(x => new MovieRating

这是该方法的完整代码以及我创建的用于将 select 字段映射到具体类型的新 class:

public class MovieRepository : IMovieRepository
{
    private readonly MovieDbContext _moviesDbContext;
    public MovieRepository(MovieDbContext moviesDbContext)
    {
        _moviesDbContext = moviesDbContext;
    }

    public IEnumerable<Movie> GetAll()
    {
        return _moviesDbContext.Movies;
    }

    public IEnumerable<MovieRating> GetTopFive()
    {
        var result = _moviesDbContext.Movies.Include(x => x.Ratings)
                    .Select(x => new MovieRating
                    {
                        Id = x.Id,
                        Title = x.Title,
                        Average = x.Ratings.Average(y => y.RatingValue)
                    }).OrderByDescending(x => x.Average).ThenBy(x => x.Title).Take(5).ToList();

        return result;
    }
}

public class MovieRating
{
    public int Id { get; set; }
    public string Title { get; set; }
    public decimal Average { get; set; }
}