Подтвердить что ты не робот

Различия между IQueryable, List, IEnumerator?

Мне интересно, что такое различие между IQueryable, List, IEnumerator и когда я должен использовать каждый?

Например, при использовании linq для sql я бы сделал что-то вроде этого

public List<User> GetUsers()
{
   return db.User.where(/* some query here */).ToList();
}

теперь мне интересно, должен ли я использовать IQueryable, но я не уверен в преимуществах использования его над списком.

4b9b3361

Ответ 1

IQueryable<T> предназначен для предоставления поставщику запросов (например, ORM, например LINQ to SQL или Entity Framework) использовать выражения, содержащиеся в запросе, для перевода запроса в другой формат. Другими словами, LINQ-to-SQL ищет свойства сущностей, которые вы используете вместе со сравнением, которые вы делаете, и на самом деле создает оператор SQL для выражения (надеюсь) эквивалентного запроса.

IEnumerable<T> более общий, чем IQueryable<T> (хотя все экземпляры IQueryable<T> реализуют IEnumerable<T>) и определяет только последовательность. Тем не менее, существуют методы расширения, доступные в классе Enumerable, которые определяют некоторые операторы типа запроса на этом интерфейсе и используют обычный код для оценки этих условий.

List<T> - это только формат вывода, и хотя он реализует IEnumerable<T>, напрямую не связан с запросом.

Другими словами, когда вы используете IQueryable<T>, вы определяете и выражаете, что переводится во что-то другое. Несмотря на то, что вы пишете код, этот код никогда не запускается, он только проверяется и превращается во что-то другое, как в реальный SQL-запрос. Из-за этого в этих выражениях действуют только определенные вещи. Например, вы не можете вызывать обычную функцию, которую вы определяете из этих выражений, поскольку LINQ-to-SQL не знает, как превратить ваш вызов в инструкцию SQL. К сожалению, большинство этих ограничений оцениваются только во время выполнения.

Когда вы используете IEnumerable<T> для запроса, вы используете LINQ-to-Objects, что означает, что вы пишете фактический код, который используется для оценки вашего запроса или преобразования результатов, поэтому, как правило, нет ограничения на то, что вы можете сделать. Вы можете свободно вызывать другие функции из этих выражений.

С LINQ to SQL

Идя рука об руку с отличием выше, важно также иметь в виду, как это получается на практике. Когда вы пишете запрос против класса контекста данных в LINQ to SQL, он создает IQueryable<T>. Независимо от того, что вы делаете против самого IQueryable<T>, он превратится в SQL, поэтому ваша фильтрация и трансформация будут выполняться на сервере. Что бы вы ни делали против этого как IEnumerable<T>, это будет сделано на уровне приложения. Иногда это желательно (например, если вам нужно использовать код на стороне клиента), но во многих случаях это непреднамеренно.

Например, если у меня был контекст со значением Customers, представляющим таблицу Customer, и каждый клиент имеет столбец CustomerId, рассмотрим два способа сделать этот запрос:

var query = (from c in db.Customers where c.CustomerId == 5 select c).First();

Это создаст SQL, который запрашивает базу данных для записи Customer с CustomerId равным 5. Что-то вроде:

select CustomerId, FirstName, LastName from Customer where CustomerId = 5

Теперь, что произойдет, если мы превратим Customers в IEnumerable<Customer> с помощью метода расширения AsEnumerable()?

var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();

Это простое изменение имеет серьезные последствия. Поскольку мы превращаем Customers в IEnumerable<Customer>, это вернет всю таблицу и отфильтрует ее на стороне клиента (ну, строго говоря, это приведет к возврату каждой строки в таблице до тех пор, пока она не встретит тот, который соответствует критике, но точка одна и та же).

ToList()

До сих пор мы говорили только о IQueryable и IEnumerable. Это потому, что они похожи, бесплатные интерфейсы. В обоих случаях вы определяете запрос; то есть вы определяете, где искать данные, какие фильтры применять и какие данные возвращать. Оба эти запроса -

query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;

Как мы уже говорили, первый запрос использует IQueryable, а второй использует IEnumerable. Однако в обоих случаях это всего лишь запрос. Определение запроса фактически ничего не делает против источника данных. Запрос действительно выполняется, когда код начинает перебирать список. Это может произойти несколькими способами; a foreach, вызов ToList() и т.д.

Запрос выполняется первым и каждый раз, когда он повторяется. Если бы вы дважды вызывали ToList() на query два раза, вы имели бы два списка с совершенно разными объектами. Они могут содержать одни и те же данные, но они будут разными ссылками.

Редактировать после комментариев

Я просто хочу быть ясно о различии между тем, когда делаются на стороне клиента и когда они сделаны на стороне сервера. Если вы ссылаетесь на IQueryable<T> как на IEnumerable<T>, только запрос после него IEnumerable<T> будет выполнен на стороне клиента. Например, скажем, у меня есть эта таблица и контекст LINQ-to-SQL:

Customer
-----------
CustomerId
FirstName
LastName

Сначала я построю запрос на основе FirstName. Это создает IQueryable<Customer>:

var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;

Теперь я передаю этот запрос функции, которая принимает IEnumerable<Customer> и выполняет некоторую фильтрацию на основе LastName:

public void DoStuff(IEnumerable<Customer> customers)
{
    foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
    {
        Console.WriteLine(cust.CustomerId);
    }
}

Мы выполнили второй запрос здесь, но это делается на IEnumerable<Customer>. Что будет происходить здесь, так это то, что первый запрос будет оценен, запустив этот SQL:

select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'

Итак, мы собираемся вернуть всех, кто FirstName начинает с "Ad". Обратите внимание, что здесь ничего нет LastName. Это потому, что он отфильтровывается на стороне клиента.

Как только он вернет эти результаты, программа затем перебирает результаты и выводит только записи, чей LastName начинается с "Ro". Недостатком этого является то, что мы вернули данные - а именно, все строки, чей LastName не начинается с "Ro" -, которые могли быть отфильтрованы на сервере.

Ответ 2

IQueryable<T>: реферат доступа к базе данных, поддерживает ленивую оценку запросов
List<T>: набор записей. Нет поддержки ленивой оценки
IEnumerator<T>: обеспечивает возможность повторения и IEnumerable<T> (которые оба IQueryable<T> и List<T>)

Проблема с этим кодом довольно проста - он всегда выполняет запрос при его вызове. Если вместо этого вы вернете db.User.Where(...) (это IQueryable<T>), вы должны провести оценку запроса до тех пор, пока он не понадобится (переименован). Кроме того, если пользователю этого метода нужно будет указать дополнительные предикаты, они также будут выполняться в базе данных, что ускоряет его выполнение.

Ответ 3

Используйте iList или List<item>, если требуется строго типизированная коллекция какого-либо объекта.

И используйте Iqueryable и Ienumurator, когда вы хотите получить немые данные в виде коллекции объектов, он будет возвращен как коллекция свободных типов и не будет применяться никаких ограничений.

Я предпочел бы использовать List<type>, потому что с помощью обертки списка и отливки в наборе с сильным типом мой результирующий набор.

Кроме того, использование списка даст вам возможность добавлять, сортировать и преобразовывать слой в Array, Ienumurator или Queryable.