У меня есть функция, целью которой является получение аукциона по его идентификатору из базы данных и включение его самой высокой ставки и типа самой высокой ставки.
Я смог получить самую высокую ставку, используя эту линию
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; }
}
🤔 А знаете ли вы, что...
C# имеет обширную библиотеку классов .NET, что упрощает разработку приложений.
Похоже, это ошибка в 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]
.