В чем разница между IQueryable<T> и IEnumerable<T>?

В чем разница между IQueryable<T> а также IEnumerable<T>?


Смотрите также В чем разница между IQueryable и IEnumerable, который перекрывается с этим вопросом.

14 ответов

Прежде всего, IQueryable<T> расширяет IEnumerable<T> интерфейс, так что все, что вы можете сделать с "простым" IEnumerable<T> Вы также можете сделать с IQueryable<T>,

IEnumerable<T> просто есть GetEnumerator() метод, который возвращает Enumerator<T> для которого вы можете назвать его MoveNext() метод, чтобы перебрать последовательность T.

Какие IQueryable<T> имеет это IEnumerable<T> в частности, это не два свойства: одно указывает на поставщика запросов (например, поставщик LINQ to SQL), а другое указывает на выражение запроса, представляющее IQueryable<T> объект как дерево абстрактного синтаксиса с возможностью выполнения во время выполнения, которое может быть понято данным поставщиком запросов (по большей части вы не можете передать выражение LINQ to SQL поставщику LINQ to Entities без генерируемого исключения).

Выражение может быть просто константным выражением самого объекта или более сложным деревом составленного набора операторов и операндов запроса. Поставщик запросов IQueryProvider.Execute() или же IQueryProvider.CreateQuery() методы вызываются с переданным ему выражением, а затем либо результатом запроса, либо другим IQueryable возвращается соответственно.

Это хорошее видео на YouTube, которое демонстрирует, как эти интерфейсы отличаются, стоит посмотреть.

Ниже приводится длинный описательный ответ.

Первый важный момент, который нужно помнить, IQueryable интерфейс наследуется от IEnumerable так что IEnumerable сможет сделать, IQueryable тоже могу сделать.

Есть много различий, но давайте поговорим об одной большой разнице, которая имеет самое большое значение. IEnumerable интерфейс полезен, когда ваша коллекция загружается с помощью LINQ или Entity Framework, и вы хотите применить фильтр к коллекции.

Рассмотрим ниже простой код, который использует IEnumerable с рамкой сущности. Это использует Where фильтр, чтобы получить записи, чьи EmpId является 2,

EmpEntities ent = new EmpEntities();
IEnumerable<Employee> emp = ent.Employees; 
IEnumerable<Employee> temp = emp.Where(x => x.Empid == 2).ToList<Employee>();

Это где фильтр выполняется на стороне клиента, где IEnumerable код есть. Другими словами, все данные извлекаются из базы данных, а затем на клиенте сканирует и получает запись с EmpId является 2,

Но теперь посмотрите код ниже, мы изменились IEnumerableв IQueryable, Он создает SQL-запрос на стороне сервера, а на стороне клиента отправляются только необходимые данные.

EmpEntities ent = new EmpEntities();
IQueryable<Employee> emp = ent.Employees;
IQueryable<Employee> temp =  emp.Where(x => x.Empid == 2).ToList<Employee>();

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

Так что если вы работаете только со сбором данных в памяти IEnumerable Это хороший выбор, но если вы хотите запросить сбор данных, который связан с базой данных, IQueryable - лучший выбор, поскольку он уменьшает сетевой трафик и использует мощь языка SQL.

Основное отличие состоит в том, что операторы LINQ для IQueryable<T> принимать Expression объекты вместо делегатов, что означает, что пользовательская логика запроса, которую он получает, например, предикат или селектор значения, имеет форму дерева выражений вместо делегата метода.

  • IEnumerable<T> отлично подходит для работы с последовательностями, которые повторяются в памяти, но
  • IQueryable<T> позволяет использовать нехватку памяти, например удаленный источник данных, например базу данных или веб-сервис.

Выполнение запроса:

  • Там, где выполнение запроса будет выполняться "в процессе", обычно все, что требуется, - это код (как код) для выполнения каждой части запроса.

  • В тех случаях, когда выполнение будет выполняться вне процесса, логика запроса должна быть представлена ​​в данных таким образом, чтобы поставщик LINQ мог преобразовать его в соответствующую форму для выполнения из нехватки памяти - будь то запрос LDAP, SQL или что-то еще.

Больше:

http://www.codeproject.com/KB/cs/646361/WhatHowWhere.jpg

IEnumerable: IEnumerable лучше всего подходит для работы с коллекцией в памяти (или локальными запросами). IEnumerable не перемещается между элементами, он только вперед.

IQueryable: IQueryable лучше всего подходит для удаленного источника данных, такого как база данных или веб-служба (или удаленные запросы). IQueryable - это очень мощная функция, которая позволяет использовать различные интересные сценарии отложенного выполнения (например, запросы на основе страниц и композиции).

Поэтому, когда вам нужно просто перебрать коллекцию в памяти, используйте IEnumerable, если вам нужно выполнить какие-либо манипуляции с коллекцией, такой как набор данных и другие источники данных, используйте IQueryable

Проще говоря, другим существенным отличием является то, что IEnumerable выполняет запрос выбора на стороне сервера, загружает данные в память на стороне клиента и затем фильтрует данные, в то время как IQueryable выполняет запрос выбора на стороне сервера со всеми фильтрами.

В реальной жизни, если вы используете ORM, такой как LINQ-to-SQL

  • Если вы создадите IQueryable, тогда запрос может быть преобразован в sql и запущен на сервере базы данных.
  • Если вы создадите IEnumerable, то все строки будут извлечены в память как объекты перед выполнением запроса.

В обоих случаях, если вы не позвоните ToList() или же ToArray() тогда запрос будет выполняться каждый раз, когда он используется, так что, скажем, у вас есть IQueryable<T> и вы заполняете 4 списка из него, тогда запрос будет выполнен к базе данных 4 раза.

Также, если вы расширяете свой запрос:

q.Where(x.name = "a").ToList()

Затем с IQueryable сгенерированный SQL будет содержать "где name =" a ", но с IEnumerable из базы данных будет извлечено гораздо больше ролей, затем проверка. X.name =" a "будет выполнена.NET.

Мы используем IEnumerable а также IQueryable манипулировать данными, полученными из базы данных. IEnumerable наследует от IQueryable, так IQueryable действительно содержит все IEnumerable функции. Основное различие между IQueryable а также IEnumerable в том, что IQueryable выполняет запрос с фильтрами, тогда как IEnumerable сначала выполняет запрос, а затем фильтрует данные на основе условий.

Найти более подробное разграничение ниже:

IEnumerable

  1. IEnumerable существует в System.Collections Пространство имен
  2. IEnumerable выполнить запрос выбора на стороне сервера, загрузить данные в память на стороне клиента и затем отфильтровать данные
  3. IEnumerable подходит для запроса данных из коллекций в памяти, таких как List, Array
  4. IEnumerable выгодно для запросов LINQ to Object и LINQ to XML

IQueryable

  1. IQueryable существует в System.Linq Пространство имен
  2. IQueryable выполняет "запрос на выборку" на стороне сервера со всеми фильтрами
  3. IQueryable подходит для запроса данных из нехватки памяти (например, удаленной базы данных, службы)
  4. IQueryable полезно для LINQ to SQL запросов

Так IEnumerable обычно используется для работы со сбором в памяти, тогда как IQueryable обычно используется для манипулирования коллекциями.

Ниже упомянутый небольшой тест может помочь вам понять один аспект различия между IQueryable<T> а также IEnumerable<T>, Я воспроизвел этот ответ из этого поста, где я пытался добавить исправления в чужой пост

Я создал следующую структуру в БД (скрипт DDL):

CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Salary] [int] NOT NULL)

Вот скрипт вставки записи (DML скрипт):

INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(1, 20)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(2, 30)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(3, 40)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(4, 50)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(5, 60)
GO

Теперь моя цель состояла в том, чтобы просто получить лучшие 2 записи из Employee таблица в базе данных. Я добавил элемент модели данных ADO.NET в консольное приложение, указывая на Employee таблица в моей базе данных и начал писать запросы LINQ.

Код для IQueryable маршрута:

using (var efContext = new EfTestEntities())
{
    IQueryable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

Когда я начал запускать эту программу, я также запустил сеанс профилировщика SQL-запросов на своем экземпляре SQL Server, и вот сводная таблица выполнения:

  1. Общее количество выполненных запросов: 1
  2. Текст запроса: SELECT TOP (2) [c].[Salary] AS [Salary] FROM [dbo].[Employee] AS [c]

Это просто IQueryable достаточно умен, чтобы применить Top (2) предложение на стороне сервера базы данных, поэтому он переносит только 2 из 5 записей по проводам. Дальнейшая фильтрация в памяти вообще не требуется на стороне клиентского компьютера.

Код для IEnumerable маршрута:

using (var efContext = new EfTestEntities())
{
    IEnumerable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

Резюме выполнения в этом случае:

  1. Общее количество выполненных запросов: 1
  2. Текст запроса, захваченный в профилировщике SQL: SELECT [Extent1].[Salary] AS [Salary] FROM [dbo].[Employee] AS [Extent1]

Теперь дело в том, IEnumerable принес все 5 записей, присутствующих в Salary таблицы, а затем выполнил фильтрацию в памяти на клиентском компьютере, чтобы получить 2 верхние записи. Таким образом, дополнительные данные (в данном случае 3 дополнительные записи) были переданы по проводам без необходимости.

IEnumerable ссылается на коллекцию, но IQueryable - это просто запрос, и он будет создан внутри дерева выражений. Мы запустим этот запрос, чтобы получить данные из базы данных.

Вот что я написал в аналогичном посте (по этой теме). (И нет, я обычно не цитирую себя, но это очень хорошие статьи.)

"Эта статья полезна: IQueryable против IEnumerable в LINQ-to-SQL.

Цитируя эту статью: "Согласно документации MSDN, вызовы, выполняемые в IQueryable, работают вместо этого путем построения внутреннего дерева выражений. "Эти методы, расширяющие IQueryable(Of T), не выполняют никаких запросов напрямую. Вместо этого их функциональность заключается в создании объекта Expression, который представляет собой дерево выражений, представляющее совокупный запрос".

Деревья выражений являются очень важной конструкцией в C# и на платформе.NET. (В целом они важны, но C# делает их очень полезными.) Чтобы лучше понять разницу, я рекомендую прочитать о различиях между выражениями и утверждениями в официальной спецификации C# 5.0 здесь. Для продвинутых теоретических концепций, которые переходят в лямбда-исчисление, выражения обеспечивают поддержку методов как первоклассных объектов. Разница между IQueryable и IEnumerable сосредоточена вокруг этой точки. IQueryable создает деревья выражений, а IEnumerable - нет, по крайней мере, в общих чертах для тех из нас, кто не работает в секретных лабораториях Microsoft.

Вот еще одна очень полезная статья, которая детализирует различия с точки зрения толчка против толчка. (Под "push" против "pull" я имею в виду направление потока данных. Методы реактивного программирования для.NET и C#

Вот очень хорошая статья, в которой подробно рассматриваются различия между лямбда-выражениями операторов и лямбда-выражениями и более подробно рассматриваются концепции выражения tress: повторное рассмотрение делегатов C#, деревьев выражений и лямбда-операторов по сравнению с лямбда-выражениями. ".

IQueryable работает быстрее, чем IEnumerable, если мы имеем дело с огромными объемами данных из базы данных, потому что IQueryable получает только необходимые данные из базы данных, а IEnumerable получает все данные независимо от необходимости из базы данных.

И IEnumerable, и IQueryable используются для хранения коллекции данных и выполнения операции манипулирования данными, например, фильтрации коллекции данных. Здесь вы можете найти лучшее отличие сравнения с примером. http://www.gurujipoint.com/2017/05/difference-between-ienumerable-and.html

По сути, под капотом детали работают сложнее, фон LINQ не такой, но вы можете посмотреть на приведенный ниже пример, который пришел мне в голову, когда я писал морской проект для «основного различия». Я постарался сделать это как можно проще.

Допустим, у нас есть торговля между двумя странами, а продавец - это мы. У нас есть 15 контейнеров следующих цветов и ctx представляет их.

СЦЕНАРИЙ-1

Мы погрузили всю насыпь из собственного порта и отправили в порт покупателя.

      var containersToLoad = ctx.Containers;              //IQueryable-Query not submitted yet
var deliveredContainers = containersToLoad.ToList();//IEnumerable-Submitted the query

На самом деле, бесполезно, но не требует какого-либо фильтра на стороне сервера или на стороне клиента. Отложенное выполнение отложит запрос до использования ToList(), Count()и другие подобные расширения. Хотя вы не используете расширение, запрос выполняется мгновенно, если вы используете foreach цикл из-за этого реализован IEnumerator интерфейс.

СОЗДАННЫЙ ЗАПРОС

      SELECT [c].[Id],[c].[Color_Id] FROM [Containers] AS [c]

СЦЕНАРИЙ-2

2.1

      var containersToLoad = from c in ctx.Containers.ToList()
                       where c.Color != Color.Green
                       select c;//IEnumerable-Submitted the query

СОЗДАННЫЙ ЗАПРОС

      SELECT [c].[Id], [c].[Color_Id] FROM [Containers] AS [c]

Хотя мы написали предложение, этот синтаксис LINQ производит точно такой же SQL-запрос, что и приведенный выше! В таком случае, почему мы зря отправляем зеленые? Мы должны исключить зеленые предметы перед отправкой.

2.2

      var containersToLoad = (from c in ctx.Containers
                       where c.Color != Color.Green
                       select c).ToList();//IEnumerable-Submitted the query

Между запросами 2.1 и 2.2 огромная разница. Предложение выполняется мгновенно в 2.2 (на сервере). Несмотря на это whereпункт, выполняющий defer в 2.1 (в клиенте). То есть по разному результат будет одинаковый. Очевидно, что 2.1 неверный путь. Также 2.2 - не лучшая практика.

СОЗДАННЫЙ ЗАПРОС

      SELECT [c].[Id],[c].[Color_Id] FROM [Containers] AS [c]
WHERE [c].[Color_Id] <> 2

2.3

      var query = from c in ctx.Containers
                       where c.Color != Color.Green
                       select c;//IQueryable-Query not submitted yet

В этом сценарии мы сначала зарезервировали места для элементов, а не загружали их. Мы создали заказ и ждем загрузки. На следующий день клиент позвонил нам.

Мы исключаем желтые из зарезервированных товаров, а в заказе осталось 5 синих контейнеров.

      query = from c in query
        where c.Color != Color.Yellow
        select c;

      var query = from c in ctx.Containers
            where c.Color != Color.Green
            select c;//IQueryable-Query not submitted yet

query = from c in query
        where c.Color != Color.Yellow
        select c;//IQueryable-Query not yet submitted

var deliveredContainers = query.ToList();//IEnumerable-Submitted the query

СОЗДАННЫЙ ЗАПРОС

      SELECT [c].[Id], [c].[Color] FROM [Containers] AS [c]
WHERE [c].[Color] NOT IN (1, 2)

Теперь основная масса выгружена в порту покупателя. Заказчик может произвести любую операцию в собственном порту. В заключение, мы не зря отправили ничего, кроме того, о чем нас просили.

ienumerable: когда мы хотим иметь дело с оперативной памятью, т. е. без связи с данными; iqueryable: когда иметь дело с сервером sql, т.е. с подключением к данным. ilist: операции, такие как добавление объекта, удаление объекта и т. д.

Другие вопросы по тегам