Как правильно составить linq запрос из бд?

Пытаюсь правильно составить запрос.
Запрос должен группировать по ApplicationUserId. А дальше получить первый объект и вернуть вместе с пользователем.

(from trainings in dbContext.TrainingObjects
 group trainings by trainings.ApplicationUserId into groups
 let bTr = groups.OrderByDescending(a => a.Index).First()
 let user = dbContext.Users.First(a => a.Id == groups.Key)
 select new
 {
     tr = bTr,
     user = user
 }).ToList()

Но вылазит ошибка:

System.InvalidOperationException: 'Processing of the LINQ expression '(GroupByShaperExpression: KeySelector: (t.ApplicationUserId), ElementSelector:(EntityShaperExpression: EntityType: TrainingObject ValueBufferExpression: (ProjectionBindingExpression: EmptyProjectionMember) IsNullable: False ) )'

public class TrainingObject
{
    [Key]
    public string Id { get; set; }
    [Required]
    public string ApplicationUserId { get; set; }
    [JsonIgnore, IgnoreDataMember, ForeignKey("ApplicationUserId")]
    public UserDetails UserDetails { get; set; }
    public double Index { get; set; }
    ...
}

public class UserDetails
{
    [Key]
    public string Id { get; set; }

    public string ApplicationUserId { get; set; }
    [ForeignKey("ApplicationUserId")]
    public ApplicationUser ApplicationUser { get; set; }
    public ICollection<TrainingObject> TrainingObjects { get; set; }
    ...
}

Попробовал в один запрос, но не дает сделать упорядочивание

(from trs in dbContext.TrainingObjects
 join user in dbContext.UsersDetails on trs.ApplicationUserId equals user.Id
 group trs by trs.ApplicationUserId into grTrs 
 select new
 {
     trs = grTrs.OrderByDescending(q=>q.Index).First(),
     user = grTrs.First().UserDetails
 }).ToList();

OrderByDescending(q => q.Index)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either


Ответы (1 шт):

Автор решения: Svyatoslav Danyliv

Для LINQ to Entities, существует пока нерушимое ограничение - после group by можно доставать только ключ группировки или результат агрегации. В вашем же случае вы хотите получить целый объект.

Это обусловлено ограничением самого SQL. Если вам нужен такой запрос, необходимо использовать чистый SQL и оконные функции. Оконные функции не поддерживаются реализацией EF любых версий.

Но, на всякий случай, оставлю вариант с использованием расширения для EF Core https://github.com/linq2db/linq2db.EntityFrameworkCore - используйте версию 3.X

Расширение использует транслятор LINQ от linq2db и не имеет ограничений присущих EF Core. Заранее предупреждаю, я создатель этого расширения.

var rnQuery = 
   from t in dbContext.TrainingObjects
   select new
   {
       RN = Sql.Ext.RowNumber().Over()
          .PartitionBy(t.ApplicationUserId)
          .OrderByDesc(t.Index)
          .ToValue(),
       tr = t,
       user = t.UserDetails.ApplicationUser
  };

var resultQuery = 
   from q in rnQuery
   where q.RN == 1
   select new
   {
       q.tr,
       q.user
   };

var result = resultQuery
   .ToLinqToDB()
   .ToList();
→ Ссылка