Сравните DateTime со строкой в ​​запросе LINQ - PullRequest
0 голосов
/ 07 марта 2019

У меня есть запрос LINQ, который фильтрует некоторые данные, сравнивая их с данным string.У моей сущности есть поле DateTime, которое мне также нужно сравнить с указанным string.

Однако мне нужно, чтобы DateTime был отформатирован как dd/MM/yyyy, а DateTime.ToString() дает m/d/yyyy.

Когда я пытаюсь разобрать DateTime с моим форматированием, я получаю следующие исключения:

Исключение типа 'System.NotSupportedException' произошло в Project1.dll, но былоне обрабатывается в пользовательском коде LINQ to Entities не распознает метод метода System.String ToString (System.String), и этот метод нельзя преобразовать в выражение хранилища.

Вот мой код:

auditLogs = auditLogs
                .Where(x => x.Username.Contains(searchValue) ||
                    x.Description.Contains(searchValue) ||
                    x.EntityType.Contains(searchValue) ||
                    x.EntityState.Contains(searchValue) ||
                    x.ChangeTime.ToString("dd/MM/yyyy").Contains(searchValue));

Есть ли обходной путь к этому?Или "правильный" способ?Я знаю, что могу просто перебрать его в цикле, но я хочу сделать это элегантно.

РЕДАКТИРОВАТЬ: Я не могу разобрать строку в DateTime, потому что строка может быть просто 03/2019или 22/02, а не полная дата.

Ответы [ 2 ]

0 голосов
/ 07 марта 2019

Очевидно, ваша последовательность AuditLogs является IQueryable, а не IEnumerable.

причина проблемы

Вы должны знать о различиях между IEnumerable и IQueryable. Объект, который реализует IEnumerable, содержит все для перечисления последовательности: вы можете попытаться получить первый элемент, и как только вы получите элемент, вы можете попытаться получить следующий элемент. Возможно, объекту потребуется вызвать некоторые функции, чтобы получить следующий запрошенный элемент.

Перечисление на самом низком уровне выполняется с использованием GetEnumerator, MoveNext и Current. На более высоком уровне вы увидите foreach или функции LINQ, такие как ToList, ToDictionary, Count, Any, Max: все функции LINQ, которые не возвращают IEnumerable<...>

Хотя IQueryable кажется похожим на IEnumerable, внутренне он работает по-другому. Объект, который реализует IQueryable, содержит Expression и Provider. Expression является общим представлением того, что должно быть запрошено. Provider знает, кто должен выполнить запрос (обычно это система управления базами данных) и какой язык использует эта СУБД (обычно что-то вроде SQL).

Когда вы начинаете перечисление (GetEnumerator), выражение отправляется поставщику, который переведет его на язык, который понимает исполнитель (SQL), и выполнил запрос. Возвращенные данные преобразуются в IEnumerator, который возвращается к MoveNext

Исполнитель не знает ваших локальных функций и поэтому не может использовать ни одну из них. На самом деле, хотя создатели Entity Framework сделали замечательную работу по переводу выражений в SQL, существует много функций LINQ, которые нельзя использовать в запросе. См. Поддерживаемые и неподдерживаемые методы LINQ (linq to entity)

К настоящему времени вы должны понимать, почему вы не можете использовать ToString().

Вернуться к вашему вопросу

Есть несколько проблем с вашим searchstring. Если оно равно 11, значит ли это месяц 11? или это означает одиннадцатый день месяца, или вы также хотите найти даты в году 2011? И вы также хотите поддержать "2010/11/30", а также "30/11/2010"?

Рассмотрите возможность преобразования значения ChangeTime в порции для года / месяца / дня, используя SqlFunctions.DateName . С помощью этой функции вы можете преобразовать строковое представление года, месяца, дня и т. Д. Из DateTime:

var result = auditLogs.Where(auditLog => ...
    || SqlFunctions.DateName("year",  auditLog.ChangeTime).Contains(searchParameter) 
    || SqlFunctions.DateName("month", auditLog.ChangeTime).Contains(searchParameter)
    || SqlFunctions.DateName("day",   auditLog.ChangeTime).Contains(searchParameter));

Это даст вам следующий результат

searchString     yields ChangeTime:
"2010/11/30"     all ChangeTimes on day 20101130
"30/11/2020"     all ChangeTimes on day 20101130
"11/30/2020"     all ChangeTimes on day 20101130

Кажется, это именно то, что вы хотите, будь то японские, европейские или американские данные. Но как насчет следующих случаев:

  • "11/2010". Вы хотите только даты ноября 2010? или также 11 января 2010 года?
  • "11". Каждый день ноября? Каждый 11-й день каждого месяца? Каждый день 2011 года?

На этот вопрос можно ответить только в том случае, если вы укажете требования однозначно.

Если вы хотите буквальный поиск, вы можете объединить извлеченные строки год / месяц / день в строку, которую вы хотите использовать, чтобы найти в строке поиска. Однако это действительно ограничит возможности поиска.

Правильный интерфейс оператора решает эти проблемы

Мой совет - сменить интерфейс оператора. Используйте что-то вроде комбо-боксов. Это гарантирует, что вы не можете указать недопустимые даты. Использование нулевого (или пустого) значения означает, что вы не хотите фильтровать нулевой год / месяц / день.

Входные данные, разделенные на год / месяц / день, облегчили бы операторам понимание синтаксиса строки поиска. Для вас запрос будет намного проще:

static IQueryable<AuditLog> Where(this IQueryable<AuditLog> auditLogs,
       int year, int month, int day)
{
     // zero value parameters: do not use. so
     // 2011 11 00: all days of November 2011
     // 2011 00 14: every 14th day of every month of 2011

     if (year != 0)
     {
         if (month != 0)
         {
             if (day != 0)
             {  // use parameters year, month, day
                return auditLogs.Where(auditLog =>
                       auditLog.ChangeTime.Year == year &&
                       auditLog.ChangeTime.Month == month &&
                       auditLog.ChangeTime.Day == day);
             }
             else
             {  // use parameters year, month
                return auditLogs.Where(auditLog =>
                       auditLog.ChangeTime.Year == year &&
                       auditLog.ChangeTime.Month == month);
             }
        }
        else
        {   // don't use Month
            if (day != 0)
            {   // use parameters year, day
                return auditLogs.Where(auditLog =>
                       auditLog.ChangeTime.Year == year &&
                       auditLog.ChangeTime.Day == day);
             }
             else
             {  // use parameter year
                return auditLogs.Where(auditLog => auditLog.ChangeTime.Year;
             }
        }
    }
    else
        ... etc, don't use parameter day        
}

Использование:

int year = comboBox.SelectedItem;     // or whatever input device you use
int month = comboBox.SelectedItem     // make sure you can't select invalid months
int day = comboBox.SelectedItem       // make sure you can't select invalid days
// zero value means: ingnore the value

var result = auditLogs
    .Where(...)
    .Where(year, month, day);
0 голосов
/ 07 марта 2019

Вы пытались создать строку из частей даты? Смотрите здесь . Кажется, похожая проблема.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...