Преобразование SQL в Linq с помощью 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

Идея предыдущего запроса состоит в том, чтобы получить 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.

Спасибо

Вы ожидаете топ-5 или только топ-5?

TanvirArjel 20.01.2019 18:28

Самый высокий рейтинг 5. Таким образом, 1 фильм может иметь одну или несколько строк в рейтинговой таблице. Например, 10 пользователей могут оценить Терминатора 1, если для этого фильма среднее значение этих оценок является максимальным по сравнению с другими фильмами, которые должны быть показаны первыми.

MarcosF8 20.01.2019 18:32

Ждать! Я обновляю ответ

TanvirArjel 20.01.2019 18:39

Рассмотрите также возможность группировки строк рейтинга и получения среднего значения.

MarcosF8 20.01.2019 18:40

Да! но я пробую другое решение.

TanvirArjel 20.01.2019 18:40

Проверьте мой ответ! Я обновил ответ. Я проверил, работает отлично.

TanvirArjel 20.01.2019 18:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
2 011
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Попробуйте следующее:

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 не содержит свойства навигации 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; }
}

Пожалуйста, дайте мне знать, если это работает или нет! Если нет, то, пожалуйста, дайте мне знать сообщение об ошибке.

TanvirArjel 20.01.2019 18:35

Я получил сообщение об ошибке: System.ArgumentException: «По крайней мере один объект должен реализовывать IComparable».

MarcosF8 20.01.2019 18:38

Ой! Я подозреваю, что! Подождите, пожалуйста!

TanvirArjel 20.01.2019 18:38

Если ваш контроллер является контроллером API, убедитесь, что поверх вашего контроллера присутствует атрибут [Produces("application/json")].

TanvirArjel 20.01.2019 19:13

еще не работает, получаю этот sql: SELECT [g.Movie0].[Id], [g.Movie0].[Title] FROM [Movies] AS [g.Movie0]

MarcosF8 20.01.2019 19:15

Мне пришлось сделать преобразование для IEnumerable

MarcosF8 20.01.2019 19:16

Давайте продолжить обсуждение в чате.

TanvirArjel 20.01.2019 19:16

@MarcosF8 Проверьте чат, пожалуйста

TanvirArjel 20.01.2019 19:19

Помогите завтра, пожалуйста!

TanvirArjel 20.01.2019 20:42

Большое спасибо, TanvirArjel, большие усилия по решению проблемы с вашей стороны, я очень ценю вашу помощь!

MarcosF8 20.01.2019 22:43

Используемый вами запрос не будет работать при определенных обстоятельствах, как я описал в комментарии к этому ответу.

TanvirArjel 21.01.2019 15:30
Ответ принят как подходящий

Попробуй это -

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

Ваш предыдущий запрос был правильным. Это вызовет Sequence contains no elements., если у фильма еще нет рейтинга! Помимо этого, в этом запросе есть еще одна проблема. Вы не использовали .Include(m => m.Ratings).

TanvirArjel 20.01.2019 19:37

@TanvirArjel Спасибо за комментарий. Я также новичок в linq и lambda. Я запрашиваю таблицу фильмов напрямую, так что не думайте, что в таблице Movie использование .GroupBy(x => new {x.Id, x.Title}) не имеет значения, поэтому я удалил его. Также включение рейтинговых данных здесь не требуется, так как ему нужен только средний рейтинг, который будет рассчитываться только в БД..??

artista_14 20.01.2019 21:24

Без включения рейтинги будут нулевыми! Следовательно, будет выброшено исключение.

TanvirArjel 20.01.2019 21:26

Include не нужен, поскольку x.Ratings уже является частью запроса. Единственное, это должен быть (int?)x.Ratings.Average(y => y.RatingValue), чтобы фильмы без рейтинга могли иметь значение null. Тогда я думаю, что это должно быть правильно, демонстрируя, что LINQ groupby не нужен. @ MarcosF8 Было бы неплохо получить немного больше отзывов, чем «не работает» :).

Gert Arnold 20.01.2019 22:27

@GertArnold Я согласен

artista_14 21.01.2019 07:11

и, наконец, это хорошо работающий код:

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(x => new MovieRating

И это полный код метода и нового класса, который я создал для сопоставления полей выбора с конкретным типом:

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

Другие вопросы по теме