Включение одного подэлемента из свойства списка при использовании ThenInclude()

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

Я смог получить самую высокую ставку, используя эту линию

var result = _context.Auctions.
      Include(a => a.AuctionBids.OrderByDescending(b => b.BidAmount).Take(1)).
      FirstOrDefault(a => a.AuctionId == id);

Когда я добавил остальные, чтобы включить тип ставки, который я использовал

  var result = _context.Auctions.
      Include(a => a.AuctionBids.OrderByDescending(b => b.BidAmount).Take(1)).ThenInclude(b => b.AuctionBidType).AsNoTracking().
      FirstOrDefault(a => a.AuctionId == id);

Но потом я получаю сообщение об ошибке

Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'AuctionBidTypeID'.
Invalid column name 'AuctionBidID'.
Invalid column name 'AuctionBidTypeID'.
Invalid column name 'BidAmount'.
Invalid column name 'BidDate'.
Invalid column name 'ExternalUserLoginID'.

Удаление .Take(1) работает, как и ожидалось, возвращая все ставки. Я пытался перемещаться .Take(1), но я просто получаю нулевой ответ или ошибку компилятора. Все, что указывает мне правильное направление, ценится

Я также проверил, используя First() вместо Take(1), это дает эту ошибку

System.InvalidOperationException: The expression 'a.AuctionBids.AsQueryable().OrderByDescending(b => b.BidAmount).First()' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.

Упрощенные классы

Это упрощенные версии моих моделей

public partial class Auction
    {
        public Auction()
        {
            AuctionBids = new HashSet<AuctionBid>();
        }

        public long AuctionId { get; set; }
        public long AuctionTypeId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string LongDescription { get; set; }
        public decimal BidIncrement { get; set; }
        public decimal BidWarningCap { get; set; }
        public decimal Reserve { get; set; }
        public int CreatedById { get; set; }
        public DateTime CreatedDate { get; set; }
        public virtual AuctionType AuctionType { get; set; }
        public virtual ICollection<AuctionBid> AuctionBids { get; set; }
    }
public partial class AuctionBid
    {
        public long AuctionBidId { get; set; }
        public long AuctionId { get; set; }
        public int ExternalUserLoginId { get; set; }
        public long AuctionBidTypeId { get; set; }
        public DateTime BidDate { get; set; }
        public decimal BidAmount { get; set; }
        public virtual Auction Auction { get; set; }
        public virtual AuctionBidType AuctionBidType { get; set; }
    }
    public partial class AuctionBidType
    {
        public AuctionBidType()
        {
            AuctionBids = new HashSet<AuctionBid>();
        }

        public long AuctionBidTypeId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public virtual ICollection<AuctionBid> AuctionBids { get; set; }
    }

Вы пробовали First() вместо Take(1)? Также я предполагаю, что включения должны выглядеть так: Include(a => a.AuctionBids.OrderByDescending(b => b.BidAmount)).ThenInclude(b => b.AuctionBidType).AsNoTracking().FirstOrDefaultAsync();

Neistow 12.05.2022 21:35

Да, я пытался использовать First() внутри Include(), это дает мне другую ошибку, с которой я обновлю вопрос. Если я использую его где-нибудь еще, это мешает FirstOrDefault в конце

mick1221 12.05.2022 22:00

Примечание: хотелось бы, чтобы все первые вопросы были такими. Слава!

Gert Arnold 13.05.2022 09:54

Вы можете легко реализовать это как 2 запроса. После того, как вы получите самую высокую ставку, получите соответствующий тип ставки. Или получите только самую высокую сумму ставки, а затем извлеките ставки с их типом ставки, которые соответствуют этой сумме. В противном случае логика включения одних и тех же условий в один запрос становится беспорядочной, как это происходит, если вы пишете те же самые условия в SQL.

pjs 13.05.2022 12:33

Исправлено в ef-core 6.

Gert Arnold 14.05.2022 09:57
Стоит ли изучать 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
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, это ошибка в EF-core 5. Сгенерированный SQL-запрос (с SQL Server) содержит неверные псевдонимы в одной строке:

SELECT [t].[AuctionId], [t].[AuctionTypeId], [t].[BidIncrement], [t].[BidWarningCap], [t].[CreatedById], [t].[CreatedDate], [t].[Description], [t].[LongDescription], [t].[Name], [t].[Reserve], [t1].[AuctionBidId], [t1].[AuctionBidTypeId], [t1].[AuctionId], [t1].[BidAmount], [t1].[BidDate], [t1].[ExternalUserLoginId], [t1].[AuctionBidTypeId0], [t1].[Description], [t1].[Name]
  FROM (
      SELECT TOP(1) [a].[AuctionId], [a].[AuctionTypeId], [a].[BidIncrement], [a].[BidWarningCap], [a].[CreatedById], [a].[CreatedDate], [a].[Description], [a].[LongDescription], [a].[Name], [a].[Reserve]
      FROM [Auctions] AS [a]
      WHERE [a].[AuctionId] = CAST(1 AS bigint)
  ) AS [t]
  OUTER APPLY (
-- Wrong aliases in line below. [t] should be [t0]
      SELECT [t].[AuctionBidId], [t].[AuctionBidTypeId], [t].[AuctionId], [t].[BidAmount], [t].[BidDate], [t].[ExternalUserLoginId], [a1].[AuctionBidTypeId] AS [AuctionBidTypeId0], [a1].[Description], [a1].[Name]
      FROM (
          SELECT TOP(1) [a0].[AuctionBidId], [a0].[AuctionBidTypeId], [a0].[AuctionId], [a0].[BidAmount], [a0].[BidDate], [a0].[ExternalUserLoginId]
          FROM [AuctionBid] AS [a0]
          WHERE [t].[AuctionId] = [a0].[AuctionId]
          ORDER BY [a0].[BidAmount] DESC
      ) AS [t0] -- This alias
      INNER JOIN [AuctionBidType] AS [a1] ON [t].[AuctionBidTypeId] = [a1].[AuctionBidTypeId]
  ) AS [t1]
  ORDER BY [t].[AuctionId], [t1].[BidAmount] DESC, [t1].[AuctionBidId], [t1].[AuctionBidTypeId0]

Когда я пытаюсь выполнить аналогичный запрос в другой модели базы данных, берется правильный псевдоним, но я не могу сделать вывод, какая часть запроса или имена классов/свойств смущают EF.

К счастью, вы можете использовать обходной путь (по крайней мере, я могу с вашей моделью):

var result = _context.Auctions.AsNoTracking()
    .Include(a => a.AuctionBids.OrderByDescending(b => b.BidAmount).Take(1))
        .ThenInclude(b => b.AuctionBidType)
    .Where(a => a.AuctionId == id)
    .AsEnumerable()
    .FirstOrDefault();

Похоже, что FirstOrDefault(a => a.AuctionId == id) — это та часть, которая сбивает с толку генератор запросов. Замена его на Where запускает запрос. Тогда остается только переключиться на вычисление в памяти (.AsEnumerable()) и применить там .FirstOrDefault().

В EF-core 6 эта ошибка исправлена. Псевдоним [t1] используется там, где в приведенном выше запросе есть [t] и [t0].

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