Сравнение даты с string Entity Framework

#c# #sql #sql-server #entity-framework

#c# #sql #sql-сервер #entity-framework

Вопрос:

Давайте предположим, что у меня есть база данных SQL Server и таблица, которая выглядит как:

 Id int NOT NULL,
Date date NOT NULL
  

У меня есть соответствующая модель entity framework:

 public class Bundle
{
    public int Id {get; set;}
    public DateTime Date {get; set;}
}
  

Пользователь может ввести строку, которая может быть любой. Мне нужно найти все элементы, где дата или любая часть даты содержит строку, введенную пользователем. Итак, в принципе, мне нужно выполнить запрос:

 SELECT Id, Date
FROM Bundles
WHERE Date LIKE '%user_query_here%'
  

Моя первая попытка была

 query.Where(b => b.Date.ToShortDateString().Contains(filter.Date))
  

Это вызывает исключение NotSupportedException, поэтому я попробовал это:

 query.Where(b => Convert.ToString(b.Date).Contains(filter.Date));
  

Пожалуйста, обратите внимание, что фильтр.Дата — это строка. Это не структура DateTime.

Это также приводит к возникновению исключения. Итак, мой вопрос в том, как выполнить запрос, написанный выше?
PS: Я не могу выполнить фильтрацию в памяти, в этой таблице много тысяч строк.

Комментарии:

1. Вы уверены в своем требовании? Мне кажется, что полагаться на строковое представление даты действительно опасно. Что, если культура между клиентом и сервером отличается?

2. Да, я полностью уверен. И я не боюсь проблем, которые могут возникнуть, если культура на сервере и клиенте отличается.

3. Вы знаете, что это будет невероятно медленно, не так ли, поскольку он не будет использовать индексы? Если бы у вас была таблица календаря, вы могли бы запустить поиск по дате по ней (гораздо меньшая таблица, с разделенными столбцами в индексах тоже), которые затем можно было бы объединить как часть обычного запроса диапазона …. но я не уверен, как с этим справиться в Entity Framework.

4. Да, я знаю об индексах. Это не имеет большого значения, потому что у меня нет индекса в этом столбце.

Ответ №1:

К сожалению, нет простого способа преобразовать datetime в строку с помощью l2e.

Вы можете использовать некоторые методы SqlFunctions

 SqlFunctions.DatePart
  

вернет значение int, представляющее часть даты (например, год, месяц, день).

и

 SqlFunctions.StringConvert
  

может помочь вам преобразовать значение int в строку (сначала преобразовав его в double), которое затем можно объединить.

Что-то вроде

 .Where(b => (SqlFunctions.StringConvert((double)SqlFunctions.DatePart("y", b)) 
             SqlFunctions.StringConvert((double)SqlFunctions.DatePart("m", b))
           //etc.
             ).Contains(filter.Date)
  

конечно, это нечитаемо и действительно не подходит для индексации.

(Намного) более простым и чистым способом, если вы работаете с sql Server, было бы создать и использовать вычисляемый столбец.

Вы можете создать его таким образом (в Google можно найти, как это сделать правильно, если вы сначала используете Code и миграции)

 alter table add StringDate as convert(varchar, [Date], 112)//or another format, this will be yyyymmdd
  

Если вы сначала используете код, вам придется пометить свойство атрибутом

 [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
  

Тогда вы сможете сделать

 .Where(b => b.StringDate.Contains(filter.Date))
  

Комментарии:

1. Большое спасибо за решение. Я использовал решение для вычисляемых столбцов.