Вы должны знать о различиях между IEnumerable
и IQueryable
.
Объект IEnumerable
содержит все для перечисления в последовательности.Вы можете запросить первый элемент, и как только у вас появится элемент, вы можете запросить следующий, если есть следующий.IEnumerable
предназначен для процессов локально вашим процессом.
Перечисление на самом низком уровне выполняется путем запроса перечислителя и повторного вызова MoveNext, пока вам больше не нужны элементы.Например:
IEnumerable<Student> students = ...
IEnumerator<Student> studentEnumerator = students.GetEnumerator();
while (studentEnumerator.MoveNext())
{
// there is still a Student to process:
Student student = studentEnumerator.Current;
ProcessStudent(student);
}
Вы можете сделать это явно или неявно вызвать его, используя foreach
или одну из функций LINQ.
С другой стороны, подразумевается IQueryable
обрабатываться другим процессом, обычно системой управления базами данных.IQueryable
содержит Expression
и Provider
.Expression
выражает запрос, который должен быть выполнен в некотором общем формате.Provider
знает, кто должен выполнить запрос (обычно это система управления базами данных), и язык, который этот процесс использует (обычно что-то вроде SQL).
Как только вы начинаете перечисление с помощью вызова GetEnumerator, Expression
отправляется Provider
, который пытается перевести Expression
в SQL и выполняет запрос.Извлеченные данные помещаются в перечисляемую последовательность и возвращается перечислитель.
Вернуться к вашему вопросу
Проблема в том, что SQL не знает ConvertPocoToModel
,Следовательно, ваш провайдер не может конвертировать Expression
.Компилятор не может обнаружить это, потому что он не знает, насколько умен ваш Provider
.Вот почему вы не получите эту ошибку, пока не наберете GetEnumerator
, в вашем случае - ToList
.
Решение
Решение состоит в том, чтобыфункция, которая изменяет выражение.Самый простой способ - это функция расширения.См. Расширение методов расширения .Таким образом, вы можете использовать его как любой другой метод LINQ:
public static IQueryable<EventModel> ToEventModels(this IQueryable<TsrEvent> tsrEvents)
{
return tsrEvent.Select(tsrEvent => new EventModel
{
Id = tsrEvent.EventId,
Name = tsrEvent.EventName,
Capacity = tsrEvent.EventCapacity,
Active = tsrEvent.EventActive
};
}
Обратите внимание, что я опускаю ()
в конструкторе: SQL не может вызывать конструкторы!
Использование:
var result = dbContext.TsrEvents
.Where(tsrEvent => tsrEvent.Active && tsrEvent.Date == Today)
.ToEventModels()
.GroupBy(...)
... etc
Или, если ваш GetEvents
вернет IQueryable<TsrEvents>
return eventRepository.GetEvents(_customerId, showInactive, showPastEvents)
.ToEventModels();
Окончательное замечание
Лучше оставить ваши данные-Функции fetch возвращают IQueryable<...>
и IEnumerable<...>
как можно дольше.Пусть только конечный пользователь материализует запрос.Было бы напрасной тратой вычислительной мощности, если вы выполните ToList()
, а ваш абонент хочет только FirstOrDefault()