Итак, у вас есть последовательность entities
, где каждый entity
имеет некоторые свойства.Вы хотите выбрать несколько (или все) элементы из этой последовательности и преобразовать каждый entity
в объект аналогичного класса.Для этого мы используем Select
Я не уверен, что ваша последовательность AsEnumerable
или AsQueryable
.Разница в том, что IQueryable
должен выполняться в другом процессе, обычно в базе данных, тогда как IEnumerable
должен выполняться в вашем локальном процессе.
AsEnumerable
Давайте сначалаопишите IEnumerable
.Предположим, что ваша входная последовательность является последовательностью Persons
, а ваш выходной - SimplePersons
.Хотя они выглядят одинаково, есть небольшая разница
class Person
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
class SimplePerson
{
public int Id {get; set;}
public string FullName {get; set;}
}
Предположим, у вас есть последовательность людей, и вы хотите преобразовать их в последовательность простых людей: каждый Person
должен стать одним SimplePerson
, где FullName
- это объединение FirstName
и LastName
.
Use 'Select to project every
Person into a
SimplePerson`:
IEnumerable<Person> persons = ...
IEnumerable<SimplePerson> simplePersons = persons
// convert every Person into a SimplePerson:
.Select(person => new SimplePerson()
{
Id = person.Id,
FullName = person.FirstName + " " + person.LastName,
});
Вуаля!Simple comme bonjour!
AsQueryable
Если ваша входная последовательность Persons AsQueryable
, то обычно запрос должен выполняться другим процессом.Это довольно часто система управления базами данных.IQueryable
имеет Expression
и Provider
.Expression
представляет запрос, который должен быть выполнен.Provider
знает, кто должен выполнить запрос (какая СУБД), он знает, как перевести Expression
на язык, который исполнитель запроса (SQL), и знает, как общаться с исполнителем.
Когда нужно перечислить IQueryable
, либо неявно с помощью foreach
, ToList()
, FirstOrDefault()
, Max()
и т. Д., Либо явно с использованием GetEnumerator
и MoveNext
, Expression
отправлено на Provider
.
Provider
переводит Expression
на язык, который СУБД понимает и выполняет запрос.Результат преобразуется в IEnumerable, для которого GetEnumerator
и MoveNext
называются
Проблема состоит в том, что SQL не знает ваш SimplePerson
, поэтому он не может вызвать его конструктор.
Одним из решений было бы позвонить ToList()
перед тем, как проецировать Persons
в SimplePersons
:
IEnumerable<SimplePerson> FetchSimplePersons()
{
IQueryable<Person> persons = ...
IEnumerable<SimplePerson> simplePersons = simplePersons
// execute the query, move persons to local process
.ToList()
// convert every Person into a SimplePerson:
.Select(person => new SimplePerson()
{
Id = person.Id,
FullName = person.FirstName + " " + person.LastName,
});
return simplePersons;
}
Однако это будет бесполезная трата или вычислительная мощность, если вы не хотите использоватьall simplePersons:
bool personsAvailable = FetchSimplePersons().Any()
Сначала вы выбираете все 1000 человек, а затем проверяете, есть ли хотя бы один SimplePerson
.Какая трата вычислительной мощности!
Для этого изобретено AsEnumerable()
.Эта функция извлекает запрашиваемые данные разумным способом, поэтому, если вы не используете все извлеченные данные, потери не слишком велики.Есть несколько стратегий, чтобы ограничить потери.Фактически используемая стратегия отличается для Provider
.
. Часто используемая стратегия для AsEnumerable
будет заключаться в получении Persons
на страницу, скажем, 100 Persons
.Если вы используете только несколько из них, вы выбросите все остальные извлеченные Persons
на странице, но по крайней мере это лучше, чем выбрасывать все 1000 Persons
IEnumerable<SimplePerson> FetchSimplePersons()
{
IQueryable<Person> persons = ...
IEnumerable<SimplePerson> simplePersons = simplePersons
// execute the query per page:
.AsEnumerable()
// convert every fetched Person into a SimplePerson:
.Select(person => new SimplePerson()
{
Id = person.Id,
FullName = person.FirstName + " " + person.LastName,
});
return simplePersons;
}
AsEnumerable()
достаточно умен, чтобы выбрать следующую страницу, если MoveNext
вызывается в последнем элементе текущей страницы.Так что для пользователей это похоже на ToList ().