Очевидно, ваша последовательность 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);