У меня есть приложение WPF, использующее EF6 и Sqlite. Я пытаюсь запустить относительно простой запрос, который сравнивает даты. Я прочитал тысячу статей о sqlite и датах, и я все еще застрял.
Я не думаю, что это имеет какое-либо отношение к временной части сравнения (т.е. попытка сравнить только дату). Я понимаю, что я мог бы использовать начальную и конечную дату и время для захвата записей, которые находятся между ними. Я думаю, что это связано с фактическим sql, который генерируется orm.
Я думаю, что проблема связана с SQL, сгенерированным EF6. В моей таблице SQlite есть поле DATETIME, которое представляет собой строковое значение в следующем формате: «2019-03-23 00:00:00.000».
Когда я запускаю запрос как часть приложения в режиме отладки VS, запрос не возвращает результатов, и я вижу из зарегистрированного sql параметр в предложении where: p__linq__0: '23/03/2019 00:00:00'
Когда я запускаю тот же самый запрос с помощью Linqpad (нацеленный на ту же базу данных с той же ссылкой на dll), запрос действительно возвращает записи, как я и ожидал, и я заметил, что sql, сгенерированный linqpad, использует этот формат для параметра @p__linq__0 DateTime2 = '2019 -03-23 00:00:00.0000000'
В приложении я ссылаюсь на System.Data.SQLite (v 1.0.110.0) — я не уверен, что использует linqpad.
Я не знаю, как повлиять на сгенерированный SQL в приложении, но linqpad предполагает, что должна быть возможность сгенерировать совместимый sql.
В случае, если это поможет или я лаю не по тому дереву, вот мой запрос linq. Сравнение проводится с параметром «сегодня», который создает другие параметры sql, как указано выше.
public IEnumerable<Menu> GetTodaysMenus()
{
DateTime today = DateTime.Now.Date;
var menus = _context.Menus
.Select(x => new
{
x,
x.SalesBusinessType,
Date = x.MenuDays.Select(y => y.CalendarDate)
})
.Where(x => x.x.DeletedAt == null &&
x.x.Published == true &&
x.Date.Contains(today))
.AsEnumerable()
.Select(x => x.x);
return menus;
}
Я могу воспроизвести сценарий и изменить конфигурацию базы данных, но не знаю, что делать. Я хочу иметь возможность использовать сравнение дат в своем приложении, как я, кажется, могу сделать при использовании linqpad.
ОБНОВИТЬ:
Этот обновленный запрос получает запись, которую я ожидал. Я до сих пор не уверен, почему и что было "не так" с оригиналом
public IEnumerable<Menu> GetTodaysMenus()
{
string today = DateTime.Now.Date.ToString("yyyy-MM-dd HH:mm:ss.fff");
var menus = _context.Menus
.Select(x => new
{
x,
x.SalesBusinessType,
x.MenuDays,
Date = x.MenuDays.Select(y => y.CalendarDate.ToString())
})
.Where(x => x.x.DeletedAt == null &&
x.x.Published == true &&
x.Date.Contains(today))
.AsEnumerable()
.Select(x => x.x);
return menus;
}
ОБНОВЛЕНИЕ 2:
Прямые сравнения работают, например, так (DateTime == сегодня) или в этом примере коллекция DateTime содержит (сегодня), однако диапазоны (т.е. datetime >= сегодня) не работают.
Также вам не нужен этот промежуточный элемент Select
, который вы можете использовать x.MenuDays.Select(y => y.CalendarDat).Contains(today)
в предложении Where
.
Как SQLite хранит дату на диске — это деталь реализации. Стандартный NET SQLite довольно хорошо возвращает даты и другие типы данных NET.
@mjwills В этом случае OP может захотеть попробовать запрос с датой, отформатированной как строка.
@mjwills - я не получаю сообщения об ошибке, скорее записи не возвращаются. Я не могу использовать строку на сегодня в запросе, так как объект имеет поле как C# DateTime. Я не знаю, что такое StartsWith, поскольку поле Date в анонимном представляет собой набор DateTime. Где нацелен на дату, которая указана анонимно, поэтому я думаю, что она должна идти после выбора.
@juharr - Тип данных CalendarDate - DateTime в С#, но я понял, что Sqlite хранит дату и время в виде строки, реальной или целочисленной. БД создается из кода с использованием этого пакета nuget, который я нашел SQLite.CodeFirst.
Я подумал, что что-то понял, когда увидел разницу в сгенерированных параметрах sql между зарегистрированным выводом sql в приложении и sql, сгенерированным при использовании linqpad...
@MakeStackOverflowGoodAgain - я надеялся, что провайдер вернет достойный объект DateTime. Я попробовал версию своего кода, в которой я возвращаю все записи в память (эта конкретная таблица небольшая). последующий запрос linq работает, и я могу получить запись. Однако я не работаю, когда запрос сразу отправляется в базу данных. По понятным причинам я не хочу всегда вытаскивать все записи в памяти.
Что, если вы попробуете что-то вроде x.MenuDays.Select(y => y.CalendarDate.ToString()).Contains(todayAsString)
. Это должно либо привести к некоторому типу SQL, который позволит вам сравнить столбец с отформатированной строкой даты, которая должна совпадать, либо EF выдаст ошибку.
my table has a DATETIME field which is a string value in the following format: "2019-03-23 00:00:00.000"
Строка не является датой, а даты не имеют формата. Если вы определяете столбец как строку, поставщик БД не сможет вам помочь (см. ссылку). Также может быть что-то еще неправильное, но сохранение дат в виде строк создает проблемы с преобразованием, а также проблемы с форматом
@MakeStackOverflowGoodAgain - я только что читал этот связь, в котором описывались три типа механизмов хранения, которые использует Sqlite, но, как вы сказали, мне все равно. Я просто упомянул об этом, если это имеет отношение к моей проблеме. Объектом в приложении является C# DateTime, и я хочу иметь дело только с DateTimes, но когда sql отправляется в базу данных напрямую, что-то вызывает несоответствие. Я просто не уверен, что.
@juharr - Итак, ваше предложение сработало. Он сгенерировал sql с форматом параметра a, который соответствовал записи... Все еще не уверен, почему и как преобразуются строки vs DateTimes... Тем не менее - спасибо.
Каков тип данных
CalendarDate
? Вы говорите, что это поле DateTime, но затем вы говорите, что это строка. БД не хранят даты и время в виде строк, но позволяют запрашивать форматированную строку.