2011-01-30 1 views
52

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

Например при использовании LINQ к SQL Я хотел бы сделать что-то вроде этого

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

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

ответ

89

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 к 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. В обоих случаях, однако, это всего лишь запрос . Определение запроса фактически ничего не делает против источника данных. Запрос действительно выполняется, когда код начинает перебирать список. Это может произойти несколькими способами; foreach цикл, вызывая ToList() и т.д.

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

Edit после комментариев

Я просто хочу, чтобы иметь четкое представление о различии между тем, когда все делается на стороне клиента, и когда они сделаны на стороне сервера. Если вы ссылаетесь на 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" - то есть может отфильтрованы на сервере.

+0

@Adam Robinson - так лучше, чтобы они могли получить возвращенный запрос, который все еще не попал в базу данных, и когда ему нужен список, он может перебирать его (а затем он попадает в источник db).Или лучше просто дать им список с места в карьер? – chobo2

+1

@ chobo2: Это зависит от того, что вы пытаетесь сделать, поэтому нет ни одного правильного ответа. В некоторых случаях может быть полезно разрешить пользователю дополнительно уточнять запрос, особенно в тех случаях, когда у вас может быть сложная логика запросов, которая является общей для нескольких разных запросов. Хотя это не говорит о том, что вы всегда должны * делать это, возвращая 'IQueryable' с базовой комплексной логикой на месте, вы сможете повторно использовать эту логику в разных местах, не дублируя ее в коде –

+0

@Adam Robinson -что бы произошло в этот сценарий. Я возвращаю список IQueryable, но мне нужно использовать IEnumerator. Я бы бросил его на это, будет ли какая-то проблема, о которой вы говорили, с Enumerable? – chobo2

8

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

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

+0

Какие еще предикаты вы говорите, что говорят, что они делают свое предложение where, которое не попадает в базу данных? Тогда этот способ, если им нужно сказать, фильтровать его еще больше, они могут без удара дБ, пока вы не сделаете итерацию над ним? – chobo2

+1

В общем, 'IQueryable ' только оценивается, если итерируется. Теперь, если кто-то должен был связать другой 'Where (...)' с ним, это также будет выполнено в базе данных. С вашим кодом он будет оцениваться в памяти. База данных предназначена для выполнения таких задач и будет делать это быстрее, чем при фильтрации данных. – Femaref

0

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

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^