Мне не удается преобразовать запрос sql в запрос linq, который мог бы вычислить некоторый запас.
Это мой тестовый запрос, который я пытаюсь преобразовать в запрос linq.
SELECT
i.*,
(SELECT COUNT(t.*) FROM tickets t
WHERE t.starttime::time = i.sessionstarttime::time
AND t.starttime::date = '2018-04-06'::date)
as stock
FROM items I
-- note that the hardcoded date ('2018-04-06') is a function parameter
(tl; dr;, как бы вы преобразовали этот запрос PostgreSQL в LINQ?)
Мои попытки пока являются вариациями следующего запроса:
var items = await _context.Items.Select(x => new Item
{
Id = x.Id,
IsTicket = x.IsTicket,
Name = x.Name,
Price = x.Price,
SaleItems = x.SaleItems,
SessionStartTime = x.SessionStartTime,
DateCreated = x.DateCreated,
DateEdit = x.DateEdit,
UserIdCreated = x.UserIdCreated,
UserIdEdited = x.UserIdEdited,
// calculate stock in subquery
Stock = _context.Tickets.Count(
t => t.StartTime.Date == ticketDate
&& x.SessionStartTime.HasValue
&& t.StartTime.Hour == x.SessionStartTime.Value.Hours // this is the part that is failing
&& t.State != TicketState.Canceled)
}).ToListAsync();
t.StartTime - это дата и время, а x.SessionStartTime - временной интервал, допускающий значение NULL.
Поэтому, когда я комментирую строку && t.StartTime.Hour == x.SessionStartTime.Value.Hours, все в порядке, но с ней я получаю предупреждения о том, что она не может быть переведена и будет оцениваться локально. Но я не хочу загружать всю таблицу билетов только для того, чтобы их посчитать.
Деталь t.StartTime.Hour в порядке, я пробовал проводить статические сравнения с обоими параметрами. t.StartTime.Hour == 5 был переведен без проблем, но x.SessionStartTime.Value.Hours == 5 не удалось перевести.
Также проблемная часть в выводе приложения:
([t].StartTime.Hour == Convert([x].SessionStartTime, TimeSpan).Hours))
Итак, я предполагаю, что эта преобразовательная часть не работает.
Итак, что мне не хватает и как я могу обойти эту проблему. Любая помощь будет оценена по достоинству.
Обновлять:
Немного поэкспериментировав, я нашел два обходных пути, на которые я бы не стал называть ответы.
Сначала я заметил, что EF пытается преобразовать Nullable<TimeSpan> в обычный TimeSpan из упомянутого вывода: ([t].StartTime.Hour == Convert([x].SessionStartTime, TimeSpan).Hours))
Я думал, что могу предотвратить это преобразование, преобразовав в строку и сравнив строки (у меня такое чувство, что это меня укусит в будущем):
t.StartTime.ToString().Contains(x.SessionStartTime.ToString())
Второй обходной путь возможен только для моего сценария, поскольку я знаю, что запрос элементов является окончательным, и я могу материализовать его без вычисленного Stock, а затем просмотреть результаты и вычислить их в отдельном запросе. Но это, похоже, добавляет дополнительные вызовы к базе данных и жертвует некоторой производительностью.
foreach(var x in items.Where(x=>x.SessionStartTime.HasValue))
{
// accessing the t.StartTime.TimeOfDay property seems to fail the LINQ to SQL as well
var hours = x.SessionStartTime.Value.Hours;
var minutes = x.SessionStartTime.Value.Minutes;
x.Stock = _context.Tickets.Count(t => t.StartTime.Date == ticketDate
&& t.StartTime.Hour == hours
&& t.StartTime.Minute == minutes);
}
Перечитывая свой вопрос: почему вы пытаетесь добиться равенства с DateTime и TimeSpan? Это не звучит логично?
Почему вы просто тестируете Hour, а не Time в LINQ по сравнению с SQL?
EF Core переводит HasValue? Может, заменить на x.SessionStartTime != null?
@NetMage: ну, я попытался сравнить Time с TimeOfDay, а также заменить HasValue на != null. Я сравниваю TimeSpan с DateTime, потому что проверяю, запускается ли билет одновременно с элементом. Теперь, после выходных, я думаю, мне нужно просто разделить поле в базе данных с DateTime на Date и Time.
Я бы использовал здесь проверку диапазона: t.StartTime > minTime && t.StartTime <= maxTime. Я не знаю, как PostgreSql работает с такими выражениями, как t.StartTime.Date, но, скорее всего, это несложно.





Вы не можете использовать «.Hours» в DateTime, допускающем значение NULL, потому что, если оно равно NULL, у него не будет свойства Hours. Вам необходимо убедиться, что DateTime не равен нулю, чтобы использовать его свойства.