简体   繁体   English

使用EF Core将SQL转换为Linq

[英]Convert SQL to Linq with EF Core

I am using .NET Core 2.2, EF Core, C# and SQL Server 2017. I am not able to translate the query I need to Linq. 我正在使用.NET Core 2.2,EF Core,C#和SQL Server 2017.我无法将我需要的查询翻译为Linq。

This is the query I need to convert: 这是我需要转换的查询:

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

The idea of the previous query is to get the Top 5 movies according to the Avg rating, ordering it by the highest average first, and in case of same average order alphabetically. 之前查询的想法是根据平均评级获得前5部电影,首先按最高平均排序,并且按字母顺序排列相同的平均顺序。

So far this is my query that makes the join, but then still missing: the group by, average, and ordering: 到目前为止,这是我的查询,使得连接,但仍然缺失:group by,average和ordering:

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;
    }
}

And these are the entities: 这些是实体:

 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; }
}

I tried to use Linqer tool also to convert my query to Linq, but it was not working. 我试图使用Linqer工具将我的查询转换为Linq,但它无法正常工作。

I will appreciate any help to convert that query to LINQ for the method "GetTopFive". 我将很感激任何帮助将该查询转换为LINQ的方法“GetTopFive”。

Thanks 谢谢

Try this one - 试试这个 -

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

Try as follows: 尝试如下:

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;
}

This will exclude the movies having no ratings. 这将排除没有评级的电影。

But if you do as follows (as artista_14's answer): 但如果你这样做(如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;
}

this will include the movies having no ratings also. 这将包括没有收视率的电影。

Note: I see your Rating model class does not contain any Movie navigation property. 注意:我看到您的Rating模型类不包含任何Movie导航属性。 Please add this as follows: 请添加如下:

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; }
}

and finally this is the code working nicely: 最后这是代码工作得很好:

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;

The problem was creating an anonymous type in the select, so this line resolves the issue: .Select(x => new MovieRating 问题是在select中创建了一个匿名类型,所以这一行解决了这个问题:.Select(x => new MovieRating

And this is the complete code for the method and the new class I have created to map the select fields with a concrete type: 这是方法的完整代码和我创建的新类,用于使用具体类型映射选择字段:

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; }
}

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

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