Вы находитесь на странице: 1из 34

Модуль 13.

Введение в LINQ.
Что такое LINQ?
LINQ (Language-Integrated Query) представляет простой и удобный язык запросов к
источнику данных. В качестве источника данных может выступать объект,
реализующий интерфейс IEnumerable (например, стандартные коллекции, массивы),
набор данных DataSet, документ XML. Но вне зависимости от типа источника LINQ
позволяет применить ко всем один и тот же подход для выборки данных.
Существует несколько разновидностей LINQ:
• LINQ to Objects: применяется для работы с массивами и коллекциями
• LINQ to Entities: используется при обращении к базам данных через технологию
Entity Framework
• LINQ to Sql: технология доступа к данным в MS SQL Server
• LINQ to XML: применяется при работе с файлами XML
• LINQ to DataSet: применяется при работе с объектом DataSet
• Parallel LINQ (PLINQ): используется для выполнения параллельных запросов
Что такое LINQ?
LINQ - технология Microsoft, предназначенная для поддержки запросов к данным
всех типов на уровне языка. Эти типы включают массивы и коллекции в памяти, базы
данных, документы XML и многое другое.
По большей части LINQ ориентирован на запросы — будь то запросы, возвращающие
набор подходящих объектов, единственный объект или подмножество полей из
объекта либо набора объектов. В LINQ этот возвращенный набор
называется последовательностью (sequence). Большинство последовательностей
LINQ имеют тип IEnumerable<T>, где T — тип данных объектов, находящихся в
последовательности. Например, если есть последовательность целых чисел, они
должны храниться в переменной типа IEnumerable<int>. Вы увидите, что
IEnumerable<T> буквально господствует в LINQ и очень многие методы LINQ
возвращают IEnumerable<T>.
Может показаться, что LINQ — это нечто, связанное только с запросами, поскольку
расшифровывается как язык интегрированных запросов (Language Integrated Query).
Однако не думайте о нем лишь в этом контексте. Предпочтительнее воспринимать
LINQ как механизм итерации данных (data iteration engine), но возможно в Microsoft
не захотели обозначать эту технологию аббревиатурой DIE ("умереть").
Ключевое слово var 

• Ключевое слово var необходимо использовать при захвате последовательности от


анонимных классов в переменную, иногда это удобный способ заставить код
компилироваться, когда возникает путаница со сложными обобщенными типами.
Хотя предпочтительнее подход к разработке, при котором точно известно, какого типа
данные содержатся в последовательности — в том смысле, что для IEnumerable<T>
должен быть известен тип T — иногда, особенно в начале работы с LINQ, это может
вводить в заблуждение. Если обнаруживается, что код не компилируется из-за
несоответствия типов данных, попробуйте заменить явно установленные типы
переменных на указанные с применением ключевым словом var.
Дерево выражения

• Дерево выражения (expression tree) - эффективное представление в древовидной


форме данных лямбда-выражения операции запроса. Эти представления деревьев
выражений могут быть вычислены все сразу, так что единственный запрос может
быть построен и выполнен на одном источнике данных, таком как база данных.
• int[] nums = new int[] { 6, 2, 7, 1, 9, 3 };
• IEnumerable<int> numsLessThanFour = nums .Where (i => i < 4) .OrderBy(i => i);
Понятие запроса
• Одним из привлекательных для разработчиков средств LINQ является SQL-подобный
синтаксис, доступный в LINQ-запросах. Этот синтаксис использовался в нескольких
примерах LINQ, приведенных ранее. Синтаксис предоставлен через расширение
языка C#, которое называется выражения запросов. Выражения запросов позволяют
запросам LINQ принимать форму, подобную SQL, всего лишь с рядом небольших
отличий.
• Для выполнения запроса LINQ выражения запросов не обязательны. Альтернативой
является использование стандартной точечной нотации C# с вызовом методов на
объектах и классах. Во многих случаях применение стандартной точечной нотации
оказывается более предпочтительным, поскольку она более наглядно демонстрирует,
что в действительности происходит и когда.
• При записи запроса в стандартной точечной нотации не происходит никакой
трансформации при компиляции. Именно поэтому во многих примерах не
используется синтаксис выражений запросов, а предпочтение отдается стандартному
синтаксису точечной нотации. Однако привлекательность синтаксиса выражений
запросов бесспорна. Впечатление чего-то знакомого, которое он производит, когда вы
формулируете свой первый запрос, может оказаться весьма привлекательным.
Понятие запроса
Выражения запросов должны подчиняться перечисленным ниже правилам:
• Выражение должно начинаться с конструкции from.
• Остальная часть выражения может содержать ноль или более конструкций from, let или
where. Конструкция from — это генератор, который объявляет одну или более переменных
диапазона, перечисляющих последовательность или соединение нескольких
последовательностей. Конструкция let представляет переменную диапазона и присваивает ей
значение. Конструкция where фильтрует элементы из входной последовательности или
соединения несколько входных последовательностей в выходную последовательность.
• Остальная часть выражения запроса может затем включать конструкцию orderby, содержащую
одно или более полей сортировки с необязательным направлением упорядочивания. Направлением
может быть ascending (по возрастанию) или descending (по убыванию).
• Затем в оставшейся части выражения может идти конструкция select или group.
• Наконец в оставшейся части выражения может следовать необязательная конструкция
продолжения. Такой конструкцией может быть либо into, ноль или более конструкций join, или же
другая повторяющаяся последовательность перечисленных элементов, начиная с конструкций из
правила 2. Конструкция into направляет результаты запроса в воображаемую выходную
последовательность, которая служит конструкцией from для последующих выражения запросов,
начиная с конструкций из правила 2.
LINQ to Objects

• Отчасти то, что делает LINQ настолько мощным и удобным в применении,


заключается в его тесной интеграции с языком C#. Вместо того, чтобы иметь дело с
полностью новым набором средств в форме классов, можно применять все те же
самые привычные коллекции и массивы с существующими классами. Это значит, что
все преимущества запросов LINQ можно получить с минимальными модификациями
существующего кода или же вовсе без них. Функциональность LINQ to Objects
обеспечивается интерфейсом IEnumerable<T>, последовательностями и
стандартными операциями запросов.
• Например, если нужно отсортировать массив целых чисел, можно выполнить запрос
LINQ для упорядочивания результатов — почти так же, как если бы это был запрос
SQL. Может существовать список ArrayList объектов Customer, в котором требуется
найти определенный объект Customer. В этом случае LINQ to Object будет
наилучшим выбором.
LINQ to Objects
IEnumerable<T> — это интерфейс, реализуемый всеми классами обобщенных коллекций C#,
как это делают массивы. Этот интерфейс позволяет выполнять перечисление элементов
коллекций.
Последовательность — это термин для обозначения коллекции, реализующей интерфейс
IEnumerable<T>. Если есть переменная типа IEnumerable<T>, то можно сказать, что имеется
последовательность элементов типа Т. Например, IEnumerable<string> означает
последовательность строк. Любая переменная, объявленная как IEnumerable<T> для типа T,
рассматривается как последовательность типа T.
Большинство стандартных операций запросов представляют собой расширяющие методы в
статическом классе System.Linq.Enumerable и прототипированы с IEnumerable<T> в качестве
первого аргумента. Поскольку они являются расширяющими методами, предпочтительно
вызывать их на переменной типа IEnumerable<T>, что позволяет синтаксис расширяющих
методов, а не передавать переменную типа IEnumerable<T> в первом аргументе.
Методы стандартных операций запросов класса System.Linq.Enumerable, не являющиеся
расширяющими методами — это просто статические методы, которые должны быть вызваны
на классе System.Linq.Enumerable. Комбинация этих методов стандартных операций запросов
дает возможность выполнять сложные запросы данных на последовательности
IEnumerable<T>.
LINQ to Objects
Унаследованные коллекции, не являющиеся обобщенными, которые существовали до
C# 2.0, поддерживают интерфейс IEnumerable, а не IEnumerable<T>. Это значит, что
нельзя непосредственно вызывать эти расширяющие методы с первым аргументом
типа IEnumerable<T> на унаследованных коллекциях. Однако можно выполнять
запросы LINQ на унаследованных коллекциях, вызывая стандартную операцию запроса
Cast или OfType на унаследованной коллекции, чтобы произвести последовательности
реализующие IEnumerable<T>, а это откроет доступ к полному арсеналу стандартных
операций запросов.
Чтобы получить доступ к стандартным операциям запросов, добавьте в код
директиву using System.Linq;, если ее еще там нет. Добавлять ссылку на сборку не
понадобится, потому что необходимый код содержится в сборке System.Core.dll,
которая автоматически добавляется к проекту средой Visual Studio 2010.
Важно помнить, что хотя многие из стандартных операций запросов прототипированы
на возврат IEnumerable<T>, и IEnumerable<T> воспринимается как последовательность,
на самом деле операции не возвращают последовательность в момент их вызова.
Вместо этого операции возвращают объект, который при перечислении выдает (yield)
очередной элемент последовательности. Во время перечисления возвращенного
объекта запрос выполняется, и выданный элемент помещается в выходную
последовательность. Таким образом, выполнение запроса откладывается.
LINQ to Objects

Методы расширения LINQ.


• Кроме стандартного синтаксиса from .. in .. select для создания запроса LINQ мы
можем применять специальные методы расширения, которые определены для
интерфейса IEnumerable. Как правило, эти методы реализуют ту же
функциональность, что и операторы LINQ типа where или orderby.
Запрос teams.Where(t=>t.ToUpper().StartsWith("Б")).OrderBy(t => t) будет аналогичен
предыдущему. Он состоит из цепочки методов Where и OrderBy. В качестве аргумента
эти методы принимают делегат или лямбда-выражение.
Список используемых методов расширения LINQ
• Select: определяет проекцию выбранных значений
• Where: определяет фильтр выборки
• OrderBy: упорядочивает элементы по возрастанию
• OrderByDescending: упорядочивает элементы по убыванию
• ThenBy: задает дополнительные критерии для упорядочивания элементов возрастанию
• ThenByDescending: задает дополнительные критерии для упорядочивания элементов по
убыванию
• Join: соединяет две коллекции по определенному признаку
• GroupBy: группирует элементы по ключу
• ToLookup: группирует элементы по ключу, при этом все элементы добавляются в словарь
• GroupJoin: выполняет одновременно соединение коллекций и группировку элементов по ключу
• Reverse: располагает элементы в обратном порядке
• All: определяет, все ли элементы коллекции удовлятворяют определенному условию
• Any: определяет, удовлетворяет хотя бы один элемент коллекции определенному условию
Список используемых методов расширения LINQ
• Contains: определяет, содержит ли коллекция определенный элемент
• Distinct: удаляет дублирующиеся элементы из коллекции
• Except: возвращает разность двух коллекцию, то есть те элементы, которые создаются только в одной коллекции
• Union: объединяет две однородные коллекции
• Intersect: возвращает пересечение двух коллекций, то есть те элементы, которые встречаются в обоих коллекциях
• Count: подсчитывает количество элементов коллекции, которые удовлетворяют определенному условию
• Sum: подсчитывает сумму числовых значений в коллекции
• Average: подсчитывает cреднее значение числовых значений в коллекции
• Min: находит минимальное значение
• Max: находит максимальное значение
• Take: выбирает определенное количество элементов
• Skip: пропускает определенное количество элементов
• TakeWhile: возвращает цепочку элементов последовательности, до тех пор, пока условие истинно
• SkipWhile: пропускает элементы в последовательности, пока они удовлетворяют заданному условию, и затем
возвращает оставшиеся элементы
• Concat: объединяет две коллекции
Список используемых методов расширения LINQ
• Zip: объединяет две коллекции в соответствии с определенным условием
• First: выбирает первый элемент коллекции
• FirstOrDefault: выбирает первый элемент коллекции или возвращает значение по
умолчанию
• Single: выбирает единственный элемент коллекции, если коллекция содердит
больше или меньше одного элемента, то генерируется исключение
• SingleOrDefault: выбирает первый элемент коллекции или возвращает значение по
умолчанию
• ElementAt: выбирает элемент последовательности по определенному индексу
• ElementAtOrDefault: выбирает элемент коллекции по определенному индексу или
возвращает значение по умолчанию, если индекс вне допустимого диапазона
• Last: выбирает последний элемент коллекции
• LastOrDefault: выбирает последний элемент коллекции или возвращает значение по
умолчанию
Операция Where

• Операция Where используется для фильтрации элементов в последовательность.


• Where принимает входную последовательность и делегат метода-предиката, а
возвращает объект, который при перечислении проходит по входной
последовательности, выдавая элементы, для которых делегат метода-предиката
возвращает true. 
Операции Select и SelectMany

• Операция Select используется для создания выходной последовательности одного


типа элементов из входной последовательности элементов другого типа. Эти типы не
обязательно должны совпадать.
• При вызове Select делегат метода-селектора передается в аргументе selector. Метод-
селектор должен принимать тип T в качестве входного, где T — тип элементов,
содержащихся во входной последовательности, и возвращать элемент типа S.
Операция Select вызовет метод-селектор для каждого элемента входной
последовательности, передав ему этот элемент. Метод-селектор выберет
интересующую часть входного элемента, создаст новый элемент — возможно,
другого типа (даже анонимного) — и вернет его.
• Обратите внимание, что метод-селектор передается через лямбда-выражение.
Операции Select и SelectMany

• Операция SelectMany используется для создания выходной последовательности с


проекцией "один ко многим" из входной последовательности. В то время как
операция Select возвращает один выходной элемент для каждого входного
элемента, SelectMany вернет ноль или более выходных элементов для каждого
входного. SelectMany получает входную последовательность элементов типа T и
делегат метода-селектора, а возвращает объект, который при перечислении проходит
по входной последовательности, получая каждый элемент индивидуально из входной
последовательности и передавая его в метод-селектор. Последний затем возвращает
объект, который во время перечисления выдает ноль или более элементов типа S в
промежуточную выходную последовательность. Операция SelectMany вернет
конкатенированную выходную последовательность при каждом вызове метода-
селектора.
• Операция SelectMany также полезна для соединения множества последовательностей
в одну.
Операции Take, TakeWhile, Skip и SkipWhile

Операции разбиения (partitioning) позволяют вернуть выходную последовательность,


которая является подмножеством входной последовательности.
• Операция Take возвращает указанное количество элементов из входной
последовательности, начиная с ее начала.
• Операция TakeWhile возвращает элементы из входной последовательности, пока
истинно некоторое условие, начиная с начала последовательности. Остальные
входные элементы пропускаются.
• Операция Skip пропускает указанное количество элементов из входной
последовательности, начиная с ее начала, и выводит остальные.
• Операция SkipWhile обрабатывает входную последовательность, пропуская
элементы до тех пор, пока условие истинно, а затем выводит остальные в выходную
последовательность.
Операция Concat

Операция Concat соединяет две входные последовательности и выдает одну выходную


последовательность.
Операции OrderBy и OrderByDescending

Операции упорядочивания позволяют выстраивать входные последовательности в


определенном порядке. Важно отметить, что и OrderBy, и OrderByDescending требуют
входной последовательности типа IEnumerable<T> и возвращают последовательность
типа IOrderedEnumerable<T>. Передавать операциям OrderBy и OrderByDescending в
качестве входной последовательности IOrderedEnumerable<T> нельзя. Причина в том,
что последующие вызовы операций OrderBy и OrderByDescending не принимают во
внимание порядок, созданный предыдущими вызовами OrderBy и OrderByDescending.
Это значит, что передавать последовательность, возвращенную из OrderBy либо
OrderByDescending, в последующий вызов операции OrderBy или OrderByDescending
не имеет смысла.
Операция OrderBy позволяет упорядочить входную последовательность на
основе метода keySelector, который возвращает значение ключа для каждого входного
элемента. Упорядоченная выходная последовательность IOrderedEnumerable<T>
выдается в порядке возрастания на основе значений возвращенных ключей.
Операции ThenBy и ThenByDescending
Вызовы ThenBy и ThenByDescending могут соединяться в цепочку, т.к. они принимают в
качестве входной последовательности IOrderedEnumerable<T> и возвращают в
качестве выходной последовательности тоже IOrderedEnumerable<T>.
Операция ThenBy позволяет упорядочивать входную последовательность типа
IOrderedEnumerable<T> на основе метода keySelector, который возвращает значение
ключа. В результате выдается упорядоченная последовательность типа
IOrderedEnumerable<T>.
В отличие от большинства операций отложенных запросов LINQ to Objects, операции
ThenBy и ThenByDescending принимают другой тип входных последовательностей
- IOrderedEnumerable<T>. Это значит, что сначала должна быть вызвана операция
OrderBy или OrderByDescending для создания последовательности
IOrderedEnumerable, на которой можно затем вызывать операции ThenBy и
ThenByDescending.
Сортировка, выполняемая операцией ThenBy, является устойчивой. Другими словами,
она сохраняет входной порядок элементов с эквивалентными ключами. Если два
входных элемента поступили в операцию ThenBy в определенном порядке, и ключевое
значение обоих элементов одинаково, то порядок тех же выходных элементов
гарантированно сохранится. В отличие от OrderBy и OrderByDescending, операции
ThenBy и ThenByDescending выполняют устойчивую сортировку.
Операции Join и GroupJoin
Операции соединения (join) связывают вместе несколько последовательностей.
Операция Join выполняет внутреннее соединение по эквивалентности двух
последовательностей на основе ключей, извлеченных из каждого элемента этих
последовательностей. 
Операция GroupJoin выполняет групповое соединение двух последовательностей на
основе ключей, извлеченных из каждого элемента последовательностей.
Операция GroupJoin работает очень похоже на Join, за исключением того, что Join
передает один элемент внешней последовательности с одним соответствующим
элементом внутренней последовательности методу resultSelector. Это значит, что
множество элементов внутренней последовательности, соответствующих одному
элементу внешней последовательности, приведут в результате к множеству вызовов
resultSelector для этого элемента внешней последовательности. В случае операции
GroupJoin все соответствующие элементы внутренней последовательности для
определенного элемента внешней последовательности передаются в resultSelector
как последовательность этого типа элемента, в результате чего метод resultSelector
вызывается только по одному разу для каждого элемента внешней
последовательности.
Операция GroupBy

• Операции группирования помогают объединять вместе элементы


последовательности по общему ключу. Операция GroupBy используется для
группирования элементов входной последовательности.
• Все прототипы операции GroupBy возвращают последовательность
элементов IGrouping<K, Т>.
Операции Distinct, Union, Except и Intersect

Операции множеств используются для выполнения математических операций с


множествами на последовательностях.
• Операция Distinct удаляет дублированные элементы из входной
последовательности.
• Операция Union возвращает объединение множеств из двух исходных
последовательностей. 
• Операция Intersect возвращает пересечение множеств из двух исходных
последовательностей.
• Операция Except возвращает последовательность, содержащую все элементы
первой последовательности, которых нет во второй последовательности. 
Операции Cast и OfType

Операции преобразования предоставляют простой и удобный способ


преобразования последовательностей в другие типы коллекций.
• Операция Cast используется для приведения каждого элемента входной
последовательности в выходную последовательность указанного типа. 
• Операция OfType используется для построения выходной последовательности,
содержащей только те элементы, которые могут быть успешно преобразованы к
указанному типу.
Операция DefaultIfEmpty

• Операция DefaultIfEmpty возвращает последовательность, содержащую элемент по


умолчанию, если входная последовательность пуста. 
• Обратите внимание, что в отличие от всех прочих операций типа элементов,
DefaultIfEmpty возвращает последовательность типа IEnumerable<T> вместо самого
типа Т. Существуют еще дополнительные операции типа элементов, но они не
включены в эту статью, поскольку не являются отложенными.
Операции Range, Repeat и Empty

Операции генерации помогают в генерации последовательностей.


• Операция Range генерирует последовательность целых чисел.
• Операция Repeat генерирует последовательность, повторяя указанный элемент
заданное количество раз. 
• Операция Empty генерирует пустую последовательность заданного типа. 
Операции ToArray и ToList

• Операция ToArray создает массив типа T из входной последовательности типа T.


• Операция ToList создает List типа T из входной последовательности типа Т. Эта
операция часто полезна для кэширования последовательности, чтобы она не могла
измениться перед ее перечислением. 
Операция ToDictionary

• Операция ToDictionary создает Dictionary типа <К,Т>, или, возможно, <К, Е>, если


прототип имеет аргумент elementSelector, из входной последовательности типа Т,
где К — тип ключа, а T — тип хранимых значений. Или же, если Dictionary имеет тип
<К, Е>, то типом хранимых значений будет Е, отличающийся от типа элементов в
последовательности — Т.
• Вы не знакомы с классом Dictionary коллекций C#, то знайте, что он позволяет
хранить элементы, которые можно извлекать по ключу. Каждый ключ должен быть
уникальным, и только один элемент может быть сохранен для одного ключа.
Элементы в Dictionary индексируются по ключу для последующего их извлечения по
соответствующим ключам. Для более подробного ознакомления с данным классом
проследуйте по приведенной выше ссылке.
Операция ToLookup

• Операция ToLookup создает объект Lookup типа <К, Т> или, возможно, <К, Е> из
входной последовательности типа T, где К — тип ключа, a T — тип хранимых
значений. Либо же, если Lookup имеет тип <К, E>, то типом хранимых значений
может быть Е, который отличается от типа элементов входной последовательности
Т.
• Хотя все прототипы операции ToLookup создают Lookup, возвращают они объект,
реализующий интерфейс ILookup. В этой статье объект, реализующий интерфейс
ILookup, обычно будет называться просто Lookup.
• Если вы незнакомы с классом Lookup коллекций C#, то знайте, что он позволяет
хранить элементы, которые могут быть извлечены по ключу. Каждый ключ должен
быть уникальным, и под одним ключом может быть сохранено множество
элементов. Обращение по индексу к Lookup с применением ключа извлекает
последовательность сохраненных с этим ключом элементов.
Операции First, FirstOrDefault, Last и LastOrDefault

Операции элементов позволяют извлекать отдельные элементы из входной


последовательности.
• Операция First возвращает первый элемент последовательности или первый
элемент последовательности, соответствующий предикату — в зависимости от
использованного прототипа.
• Операция FirstOrDefault подобна First во всем, кроме поведения, когда элемент не
найден. 
• Операция Last возвращает последний элемент последовательности или последний
элемент, соответствующий предикату — в зависимости от используемого
прототипа.
• Операция LastOrDefault подобна Last во всем, за исключением поведения в случае,
когда элемент не найден. Эта операция имеет два прототипа, анологичные
операции Last.
Операции Any, All и Contains

• Операция Any возвращает true, если любой из элементов входной


последовательности отвечает условию.
• Операция All возвращает true, если каждый элемент входной последовательности
отвечает условию.
• Операция Contains возвращает true, если любой элемент входной
последовательности соответствует указанному значению. 
Операции Count, LongCount и Sum

• Операция Count возвращает количество элементов во входной


последовательности.
• Операция LongCount возвращает количество элементов входной
последовательности как значение типа long. Эта операция имеет два прототипа,
полностью идентичных прототипам операции Count, за тем лишь исключением, что
они возвращают число элементов, имеющих тип long, а не int.
• Операция Sum возвращает сумму числовых значений, содержащихся в элементах
последовательности. 
Спасибо за внимание!
Вопросы.

Вам также может понравиться