Общие сведения о LINQ
LINQ (Language-Integrated Query) предоставляет возможности выполнения запросов на уровне языка и API функции высшего порядка в C# и Visual Basic для написания выразительного декларативного кода.
Синтаксис запросов на основе языка
Это синтаксис запросов на основе языка:
Это тот же пример, использующий API IEnumerable :
LINQ является выразительной методикой
Это традиционный императивный код:
Цель написания кода заключается не только в создании нового Dictionary и его добавления с помощью цикла, но также в преобразовании существующего списка в словарь! LINQ позволяет выполнить эту задачу, тогда как принудительный код — нет.
Это эквивалентное выражение LINQ:
Код, использующий LINQ, является весьма удобным, так как он создает равные условия как для достижения цели, как и для написания кода, сохраняя при этом логику. Еще одним преимуществом является краткость кода. Большие части базы кода можно сократить на треть, как показано выше. Неплохо, правда?
Поставщики LINQ упрощают доступ к данным
Использование значительной части существующего ПО связано с обработкой данных из определенного источника (баз данных, JSON, XML и т. д.). Часто для этого требуется изучать новый API по каждому источнику данных, что может оказаться раздражающим фактором. LINQ упрощает эту задачу путем абстрагирования общих элементов доступа к данным в синтаксис запросов, который имеет один и тот же вид независимо от выбираемого источника данных.
При этом будут найдены все элементы XML с указанным значением атрибута:
Написать код для просмотра XML-документа вручную будет намного сложнее.
Поставщики LINQ можно использовать для реализации целого ряда задач, не ограничиваясь только взаимодействием с XML. LINQ to SQL является довольно минималистичным инструментом объектно-реляционного сопоставления (ORM) для базы данных сервера MSSQL. Библиотека Json.NET предоставляет эффективные возможности просмотра документов JSON с помощью LINQ. Кроме того, если библиотека с необходимыми вам функциями отсутствует, можно написать собственный поставщик LINQ!
Причины использования синтаксиса запроса
Зачем использовать синтаксис запроса? Этот вопрос возникает довольно часто. В конце концов, следующий код:
гораздо более лаконичен, чем этот:
Может быть, синтаксис API просто является самым кратким способом формирования синтаксиса запроса?
Нет. Синтаксис запроса позволяет использовать предложение let, которое дает возможность ввести и привязать переменную в области выражения и применять ее в последующих частях выражения. Можно воспроизвести тот же код только с помощью синтаксиса API, но, скорее всего, этот код будет трудночитаемым.
Поэтому возникает вопрос о том, можно ли просто использовать синтаксис запросов?
Ответом будет Да, если.
Ответом будет Нет, если.
Важные части LINQ
Полный список примеров LINQ см. на странице 101 LINQ Samples (101 пример LINQ).
Далее приводятся примеры демонстрации некоторых важных частей LINQ. Она не является исчерпывающей, так как LINQ предоставляет больше возможностей, чем показано здесь.
Спрямление списка списков:
Объединение двух наборов (с пользовательским блоком сравнения):
Пересечение двух наборов:
Упорядочение
Равенство свойств экземпляра
И, наконец, расширенный пример: определение равенства значений свойств двух экземпляров одного типа (взят и изменен на основе этой записи на сайте StackOverflow):
PLINQ
Рассмотрим следующий пример.
Этот код будет секционировать facebookUsers по потокам системы, суммировать общие лайки в каждом параллельном потоке, суммировать результаты, вычисленные каждым потоком, и выводить результат в виде понятной строки.
Параллелизуемые задания, использующие ресурсы ЦП, которые можно легко выразить через LINQ (другими словами, чистые функции без побочных эффектов) являются отличным кандидатом для PLINQ. Для работы с заданиями, которые имеют побочный эффект, рекомендуется рассмотреть возможность использования библиотеки параллельных задач.
Using system 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:
Простейшее определение запроса LINQ выглядит следующим образом:
Далее с помощью оператора where проводится фильтрация объектов, и если объект соответствует критерию (в данном случае начальная буква должна быть «Б»), то этот объект передается дальше.
Оператор orderby упорядочивает по возрастанию, то есть сортирует выбранные объекты.
Оператор select передает выбранные значения в результирующую выборку, которая возвращается LINQ-выражением.
Преимуществом подобных запросов также является и то, что они интуитивно похожи на запросы языка SQL, хотя и имеют некоторые отличия.
Методы расширения LINQ
Запрос teams.Where(t=>t.ToUpper().StartsWith(«Б»)).OrderBy(t => t) будет аналогичен предыдущему. Он состоит из цепочки методов Where и OrderBy. В качестве аргумента эти методы принимают делегат или лямбда-выражение.
Не каждый метод расширения имеет аналог среди операторов LINQ, но в этом случае можно сочетать оба подхода. Например, используем стандартный синтаксис linq и метод расширения Count(), возвращающий количество элементов в выборке:
Список используемых методов расширения LINQ
Select : определяет проекцию выбранных значений
Where : определяет фильтр выборки
OrderBy : упорядочивает элементы по возрастанию
OrderByDescending : упорядочивает элементы по убыванию
ThenBy : задает дополнительные критерии для упорядочивания элементов возрастанию
ThenByDescending : задает дополнительные критерии для упорядочивания элементов по убыванию
Join : соединяет две коллекции по определенному признаку
GroupBy : группирует элементы по ключу
ToLookup : группирует элементы по ключу, при этом все элементы добавляются в словарь
GroupJoin : выполняет одновременно соединение коллекций и группировку элементов по ключу
Reverse : располагает элементы в обратном порядке
All : определяет, все ли элементы коллекции удовлятворяют определенному условию
Any : определяет, удовлетворяет хотя бы один элемент коллекции определенному условию
Contains : определяет, содержит ли коллекция определенный элемент
Distinct : удаляет дублирующиеся элементы из коллекции
Except : возвращает разность двух коллекцию, то есть те элементы, которые создаются только в одной коллекции
Union : объединяет две однородные коллекции
Intersect : возвращает пересечение двух коллекций, то есть те элементы, которые встречаются в обоих коллекциях
Count : подсчитывает количество элементов коллекции, которые удовлетворяют определенному условию
Sum : подсчитывает сумму числовых значений в коллекции
Average : подсчитывает cреднее значение числовых значений в коллекции
Min : находит минимальное значение
Max : находит максимальное значение
Take : выбирает определенное количество элементов
Skip : пропускает определенное количество элементов
TakeWhile : возвращает цепочку элементов последовательности, до тех пор, пока условие истинно
SkipWhile : пропускает элементы в последовательности, пока они удовлетворяют заданному условию, и затем возвращает оставшиеся элементы
Concat : объединяет две коллекции
Zip : объединяет две коллекции в соответствии с определенным условием
First : выбирает первый элемент коллекции
FirstOrDefault : выбирает первый элемент коллекции или возвращает значение по умолчанию
Single : выбирает единственный элемент коллекции, если коллекция содердит больше или меньше одного элемента, то генерируется исключение
SingleOrDefault : выбирает первый элемент коллекции или возвращает значение по умолчанию
ElementAt : выбирает элемент последовательности по определенному индексу
ElementAtOrDefault : выбирает элемент коллекции по определенному индексу или возвращает значение по умолчанию, если индекс вне допустимого диапазона
Last : выбирает последний элемент коллекции
LastOrDefault : выбирает последний элемент коллекции или возвращает значение по умолчанию
Работа с запросами LINQ
Введение
Вы освоите эти методы на примере приложения, которое демонстрирует один из основных навыков любого иллюзиониста: тасовка по методу фаро. Так называют метод тасовки, при котором колода делится ровно на две части, а затем собирается заново так, что карты из каждой половины следуют строго поочередно.
Этот метод очень удобен для иллюзионистов, поскольку положение каждой карты после каждой тасовки точно известно, и через несколько циклов порядок карт восстанавливается.
Здесь же он используется в качестве не слишком серьезного примера для процессов управления последовательностями данных. Приложение, которое вы создадите, будет моделировать колоду карт и выполнять для них серию тасовок, выводя новый порядок карт после каждой из них. Вы сможете сравнить новый порядок карт с исходным.
Это руководство описывает несколько шагов. После каждого из них вы сможете запустить приложение и оценить результаты. Готовый пример доступен в репозитории dotnet/samples на сайте GitHub. Инструкции по загрузке см. в разделе Просмотр и скачивание примеров.
Предварительные требования
Создание приложения
Если вы раньше никогда не работали с C#, изучите структуру программы C# по этому руководству. Мы рекомендуем сначала ознакомиться с ним, а затем вернуться сюда и продолжить изучение LINQ.
Создание набора данных
Если эти три строки (инструкции using ) находятся не в верхней части файла, наша программа не будет компилироваться.
Теперь, когда у вас есть все необходимые ссылки, посмотрите, из чего состоит колода карт. Как правило, в колоде игральных карт четыре масти и в каждой масти по тринадцать значений. Вы можете создать класс Card сразу же и заполнить коллекцию объектами Card вручную. С помощью LINQ колоду карт можно создать гораздо быстрее, чем обычным способом. Вместо класса Card вы можете создать две последовательности, представляющие масти и ранги соответственно. Вы создадите два очень простых метода итератора, которые будут создавать ранги и масти как IEnumerable строк:
Несколько выражений from создают запрос SelectMany, который формирует одну последовательность из сочетаний каждого элемента первой последовательности с каждым элементом второй последовательности. Для нашего примера важен порядок последовательности. Первый элемент первой последовательности (масти) поочередно сочетается с каждым элементом второй последовательности (ранги). В итоге мы получаем все тринадцать карт первой масти. Этот процесс повторяется для каждого элемента первой последовательности (масти). Конечным результатом является колода карт, упорядоченная сначала по мастям, а затем по достоинствам.
Важно помнить, что независимо от того, будете ли вы записывать LINQ в синтаксисе запросов, показанном выше, или вместо этого будете использовать синтаксис методов, вы всегда можете перейти от одной формы синтаксиса к другой. Приведенный выше запрос, записанный в синтаксисе запросов, можно записать в синтаксис метода следующим образом:
Компилятор преобразует инструкции LINQ, написанные с помощью синтаксиса запросов, в эквивалентный синтаксис вызова метода. Таким образом, независимо от выбранного синтаксиса, две версии запроса дают одинаковый результат. Выберите, какой синтаксис лучше всего подходит для вашей ситуации. Например, если вы работаете в команде, в которой у некоторых участников есть сложности с синтаксисом метода, попробуйте использовать синтаксис запроса.
Обработка порядка
Теперь рассмотрим, как вы будете тасовать карты в колоде. Чтобы хорошо потасовать, сначала необходимо разделить колоду на две части. Эту возможность вам предоставят методы Take и Skip, входящие в интерфейсы API LINQ. Поместите их под циклом foreach :
В стандартной библиотеке нет метода для тасовки, которым можно было бы воспользоваться, поэтому вам нужно написать собственный. Метод для тасовки, который вы создадите, иллюстрирует несколько приемов, которые вы будете использовать в программах на основе LINQ, поэтому каждая часть этого процесса будет описана в действиях.
Обратите внимание на сигнатуру метода, в частности на параметры:
Так выглядит реализация этого метода:
Теперь, добавив в проект этот метод, вернитесь к методу Main и один раз перетасуйте колоду:
Сравнение
Через сколько тасовок колода снова соберется в исходном порядке? Чтобы узнать это, вам нужно написать метод, который проверяет равенство двух последовательностей. Создав такой метод, вы поместите код тасовки колоды в цикл, в котором будете проверять, расположены ли карты в правильном порядке.
Здесь мы видим в действии второй принцип LINQ: терминальные методы. Они принимают последовательность в качестве входных данных (или две последовательности, как в нашем примере) и возвращают скалярное значение. В цепочке методов для запроса LINQ терминальные методы всегда используются последними, отсюда и название «терминальный».
Выполните код и обратите внимание на то, как выполняется переупорядочивание колоды при каждой тасовке. После 8 тасовок (итераций цикла do-while), колода возвращается к исходной конфигурации, в которой она находилась при создании из начального запроса LINQ.
Оптимизация
Пример, который вы создали к этому моменту, выполняет внутреннюю тасовку, то есть первая и последняя карты колоды сохраняют свои позиции после каждой итерации. Давайте внесем одно изменение: вместо этого мы будем использовать внешнюю тасовку, при которой все 52 карты изменяют свои позиции. Для этого колоду нужно собирать так, чтобы первой картой в колоде стала первая карта из нижней половины. Тогда самой нижней картой станет последняя карта из верхней половины колоды. Это простое изменение в одной строке кода. Обновите текущий запрос тасовки, переключив положения Take и Skip. Это поменяет местами нижнюю и верхнюю половины колоды:
Снова запустите программу, и вы увидите, что для восстановления исходного порядка теперь требуется 52 итерации. Также вы могли обратить внимание, что по мере выполнения программы она заметным образом замедляется.
Для этого есть сразу несколько причин. Вы можете решить одну из самых существенных причин спада производительности — неэффективное использование отложенного вычисления.
Короче говоря, отложенное вычисление означает, что вычисление инструкции не выполняется, пока не понадобится ее значение. Запросы LINQ — это инструкции, которые обрабатываются отложенным образом. Последовательности создаются только тогда, когда происходит обращение к их элементам. Обычно это дает LINQ огромное преимущество. Но в некоторых программах, таких как в нашем примере, это приводит к экспоненциальному росту времени выполнения.
Помните, что мы создали исходную колоду с помощью запроса LINQ. Каждая последующая тасовка выполняет три запроса LINQ к колоде, полученной на предыдущем этапе. И все эти запросы выполняются отложенно. В частности, это означает, что запросы выполняются каждый раз при обращении к последовательности. Таким образом, пока вы доберетесь до 52-й итерации, исходная колода будет заново создана очень много раз. Чтобы наглядно это продемонстрировать, давайте создадим журнал выполнения. Затем вы исправите эту проблему.
В вашем файле Extensions.cs введите или скопируйте приведенный ниже метод. Этот метод расширения создает файл с именем debug.log в каталоге проекта и записывает в файл журнала, какой запрос выполняется в данный момент. Этот метод расширения можно добавить к любому запросу, чтобы зафиксировать его выполнение.
Это позволит решить проблему, и красная линия ошибки исчезнет.
Теперь давайте дополним определение каждого запроса сообщением для журнала:
Обратите внимание, что запись в журнал не нужно выполнять при обращении к запросу. Она выполняется только при создании исходного запроса. Программа по-прежнему работает очень долго, но теперь вы хорошо видите, почему. Если у вас не хватит терпения выполнять внешнюю тасовку с ведением журнала, переключите программу обратно на внутреннюю тасовку. На ней вы также заметите влияние отложенного вычисления. За один запуск программа выполняет 2592 запроса, если учитывать все создания мастей и достоинств.
Вы можете повысить производительность кода, чтобы уменьшить количество выполнений. Простой способ исправить — кэшировать результаты исходного запроса LINQ, который создает колоду карт. В настоящее время вы выполняете запросы снова и снова каждый раз, когда цикл do-while проходит через итерацию, повторно создавая и перетасовывая колоду карт. Чтобы кэшировать колоду карт, вы можете использовать методы LINQ ToArray и ToList. Когда вы добавляете их в запросы, они будут выполнять те действия, которые вы указали, но теперь они будут хранить результаты в массиве или списке в зависимости от того, какой метод вы вызовете. Добавьте метод LINQ ToArray в оба запроса и снова запустите программу:
Теперь при внутренней тасовке выполняется всего 30 запросов. Переключите программу на внешнюю тасовку, и вы заметите аналогичное улучшение: теперь выполняется 162 запроса.
На практике некоторые алгоритмы хорошо работают с упреждающим вычислением, а другие хорошо выполняются с отложенным вычислением. Для ежедневного использования отложенное вычисление обычно дает более хороший результат, если в качестве источника данных используется отдельный процесс, например база данных. Для баз данных отложенное вычисление позволяет сложным запросам выполнять только один круговой путь к процессу базы данных и обратно к оставшемуся коду. LINQ является гибким, независимо от того, используете ли вы отложенное или упреждающее вычисление, поэтому измерьте процессы и выберите тип вычислений, который обеспечивает наилучшую производительность.
Заключение
В этом проекте вы изучили:
Помимо LINQ вы узнали об использовании метода, который иллюзионисты используют для карточных фокусов. Они используют тасовку по методу Фаро, потому что она позволяет хорошо контролировать положение каждой карты в колоде. Теперь, когда вы все это знаете, не рассказывайте это остальным!
Дополнительные сведения о LINQ см. в следующих статьях:
System. Linq Пространство имен
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Предоставляет классы и интерфейсы, поддерживающие запросы с использованием LINQ.
Классы
Представляет дерево выражений и предоставляет функциональные возможности для выполнения дерева выражения после его перезаписи.
Представляет дерево выражений и предоставляет функциональные возможности для выполнения дерева выражения после его перезаписи.
Представляет IEnumerable в виде источника данных EnumerableQuery.
Представляет коллекцию ключей, каждый из сопоставляется с одним или несколькими значениями.
Представляет отсортированную параллельную последовательность.
Предоставляет набор методов для выполнения запросов к объектам, реализующим ParallelQuery
Представляет параллельную последовательность.
Представляет параллельную последовательность.
Интерфейсы
Представляет коллекцию объектов с общим ключом.
Определяет индексатор, свойство размера и метод логического поиска для структур данных, сопоставляющих ключи с последовательностями IEnumerable значений.
Представляет упорядоченную последовательность.
Представляет результат операции сортировки.
Представляет результат операции сортировки.
Предоставляет функциональные возможности для оценки запросов по определенным источникам данных в случае, если тип данных не указан.
Предоставляет функциональные возможности для оценки запросов по определенным источникам данных в случае, если тип данных известен.
Определяет методы создания и выполнения запросов, описываемых объектом IQueryable.
Перечисления
Режим выполнения запросов представляет собой рекомендацию, сообщающую системе, каким образом следует обрабатывать компромиссы производительности при параллелизации запросов.
Указывает предпочтительный тип слияния выходных данных для использования в запросе. Другими словами, указывает, каким образом PLINQ должен объединять результаты из различных разделов в одну итоговую последовательность. Это только рекомендация, которую система может не соблюдать при параллелизации всех запросов.
Комментарии
System.LinqПространство имен находится в сборке System. Core (в System.Core.dll).
Дополнительные сведения см. в разделе LINQ to SQL.
LINQ — (C#)
Аббревиатура LINQ обозначает целый набор технологий, создающих и использующих возможности интеграции запросов непосредственно в язык C#. Традиционно запросы к данным выражаются в виде простых строк без проверки типов при компиляции или поддержки IntelliSense. Кроме того, разработчику приходится изучать различные языки запросов для каждого типа источников данных: баз данных SQL, XML-документов, различных веб-служб и т. д. Технологии LINQ превращают запросы в удобную языковую конструкцию, которая применяется аналогично классам, методам и событиям. Вы создаете запросы к строго типизированным коллекциям объектов с помощью ключевых слов языка и знакомых операторов. Семейство технологий LINQ обеспечивает согласованное функционирование запросов для объектов (LINQ to Objects), реляционных баз данных (LINQ to SQL) и XML (LINQ to XML).
На приведенном ниже рисунке показан частично выполненный запрос LINQ к базе данных SQL Server в C# и Visual Basic с полной проверкой типов и поддержкой IntelliSense:
Описание выражения запроса
Следующие шаги
Чтобы получить дополнительные сведения о LINQ, сначала ознакомьтесь с некоторыми основным понятиями в статье Основы выражения запроса, а затем переходите к документации по интересующей вас технологии LINQ.
Чтобы глубже разобраться в базовой концепции LINQ изучите статью о LINQ в C#.
Чтобы быстрее приступить к работе с LINQ в C#, переходите к руководству Работа с LINQ.


