Различия между IQueryable, List, IEnumerator? - PullRequest
61 голосов
/ 30 января 2011

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

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

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

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

Ответы [ 3 ]

107 голосов
/ 30 января 2011

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

8 голосов
/ 30 января 2011

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

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

0 голосов
/ 06 июля 2016

Используйте iList или List<item>, если вы хотите строго типизированную коллекцию некоторой сущности.

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

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

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

...