#c# #postgresql #entity-framework-core #npgsql #nodatime
#c# #postgresql #entity-framework-core #npgsql #nodatime
Вопрос:
В моих проектах .NET Core 3.1 я в настоящее время переношу свой код, связанный со временем, в NodaTime.
У меня возникли проблемы с переводом некоторых запросов, потому что разница между двумя моментами времени в NodaTime дает длительность, в то время как в Postgres разница между двумя временными метками дает ИНТЕРВАЛ.
Npgsql сопоставляет мгновение с МЕТКОЙ ВРЕМЕНИ, но ИНТЕРВАЛ — это период.
Допустим, у меня есть объект с именем «Визит», который имеет идентификатор и два раза: «прибыл» и «ушел». (Это не мой фактический вариант использования, у меня есть несколько объектов с этой проблемой, так что это минимальный пример)
public class Visit
{
public int Id { get; set; }
public Instant Arrived { get; set; }
public Instant Left { get; set; }
}
Теперь я хочу найти посещения, в которых посетитель оставался меньше заданного промежутка времени:
public async Task<List<Visit>> FindVisitsShorterThan(DbSet<Visit> dbSet, Duration duration)
{
var visits =
from visit in dbSet
where (visit.Left - visit.Arrived) < duration
select visit;
return await visits.ToListAsync();
}
Если я это сделаю, Npgsql пожалуется на что-то вроде System.InvalidCastException: Can't write CLR type NodaTime.Duration with handler type TimestampHandler
— потому что длительности не поддерживаются.
С другой стороны, если я преобразую длительность в период, NodaTime жалуется, потому что, с его точки зрения, эта разница является длительностью, и ее нельзя сравнить с < с периодом.
Я мог бы сделать пару вещей, но ни одна из них не кажется идеальной:
- Измените сущность и замените «Instant Left» на «Period Stay», чтобы мне не приходилось выполнять это вычисление в запросе. Но это будет только для одного конкретного запроса — что, если мне нужно «Оставить» для другого запроса?
- Используйте только LocalDateTime вместо Instant . Разница заключается в периоде, так что это будет работать как в NodaTime, так и в Postgres. Но таким образом я меняю семантику своей сущности — я хотел, чтобы эти времена были временными метками UTC по какой-то причине. LocalDateTime по сути означает «TZ неизвестно или хранится в другом месте», что просто неверно.
- Вернитесь к типам BCL и снова начните бороться с часовыми поясами.
- Разделите мои сущности на типы доменов с мгновенными и специфичными для Npgsql типами DTO с LocalTime и выполните некоторое сопоставление между ними. Вероятно, лучшее решение, но кажется ненужным.
Есть ли лучший способ?
Комментарии:
1. Хм. Я ничего не знаю о том, как Npgsql преобразует запрос… Я подозреваю, что лучшим решением было бы использовать это преобразование для обработки вашего существующего запроса путем преобразования
Duration
в PostgresInterval
, где это необходимо. Я подозреваю , что вы не сможете легко получить хороший код без изменения Npgsql.2. Даже если бы существовал способ преобразовать выражение в запрос, результирующий запрос был бы неаргументируемым. Вам было бы намного лучше предварительно вычислить это значение и сохранить его в отдельном столбце.
Ответ №1:
Это действительно досадное ограничение в текущей поддержке Npgsql EF для типов NodaTime.
Хорошей новостью является то, что я только что реализовал вышеупомянутое вместе с некоторыми другими арифметическими переводами NodaTime — см. Эту проблему с отслеживанием . Это будет включено в предстоящий выпуск EF Core 5.0 на следующей неделе — просто подождите несколько дней, и все должно быть хорошо.
Комментарии:
1. Итак … это мой первый вопрос о stackoverflow. Я не ожидал многого. Но в течение двух дней я получил комментарий от Джона Скита, и моя проблема была решена самим владельцем Npgsql. Большое вам спасибо! Я все равно готовился к обновлению до .NET 5, так что это тоже не могло прийти в лучшее время.