Language Integrated Query ( LINQ , произносится как «линк») — компонент Microsoft .NET Framework , который добавляет собственные возможности запросов к данным в языки .NET , первоначально выпущенный как основная часть .NET Framework 3.5 в 2007 году.
LINQ расширяет язык добавлением выражений запросов , которые похожи на операторы SQL и могут использоваться для удобного извлечения и обработки данных из массивов , перечислимых классов , XML- документов, реляционных баз данных и сторонних источников данных. Другие применения, которые используют выражения запросов в качестве общей структуры для читаемого составления произвольных вычислений, включают построение обработчиков событий [1] или монадических парсеров . [2] Он также определяет набор имен методов (называемых стандартными операторами запросов или стандартными операторами последовательностей ), а также правила трансляции, используемые компилятором для трансляции выражений синтаксиса запросов в выражения с использованием стиля fluent (называемого синтаксисом методов Microsoft) с этими именами методов, лямбда-выражениями и анонимными типами .
В дальнейшем описания операторов основаны на применении работы с коллекциями. Многие операторы принимают другие функции в качестве аргументов. Эти функции могут быть предоставлены в форме именованного метода или анонимной функции.
Набор операторов запросов , определенных LINQ, предоставляется пользователю как API Standard Query Operator (SQO) . Операторы запросов, поддерживаемые API: [3]
Эти операторы опционально принимают функцию, которая извлекает определенное числовое значение из каждого элемента в коллекции и использует его для нахождения суммы, минимального, максимального или среднего значения всех элементов в коллекции соответственно. Перегруженные версии не принимают никакой функции и действуют так, как будто идентификатор задан как лямбда.
Обобщенная сумма / минимум / максимум. Этот оператор принимает функцию, которая определяет, как два значения объединяются для формирования промежуточного или конечного результата. При желании можно указать начальное значение, что позволяет сделать тип результата агрегации произвольным. Кроме того, можно указать функцию завершения, которая преобразует результат агрегации в еще одно значение. Это реализует функцию высшего порядка Fold .
IGrouping<Key, Values>
объектов для каждого отдельного ключевого значения. IGrouping
Затем объекты можно использовать для перечисления всех объектов для конкретного ключевого значения.Стандартный API оператора запроса также определяет определенные операторы, которые преобразуют коллекцию в другой тип: [3]
IEnumerable<T>
. [4]IQueryable<T>
.T[]
из коллекции.List<T>
из коллекции.Dictionary<K, T>
из коллекции, индексированной по ключу K. Указанная пользователем функция проекции извлекает ключ из каждого элемента.Lookup<K, T>
из коллекции, индексированной по ключу K. Указанная пользователем функция проекции извлекает ключ из каждого элемента.IEnumerable
коллекцию в одну из , IEnumerable<T>
приводя каждый элемент к типу T
. Поочередно преобразует универсальную коллекцию IEnumerable<T>
в другую универсальную коллекцию IEnumerable<R>
, приводя каждый элемент из типа T
в тип R
. Выдает исключение в любом элементе, который не может быть приведен к указанному типу.IEnumerable
коллекцию в одну из IEnumerable<T>
. Поочередно преобразует универсальную коллекцию IEnumerable<T>
в другую универсальную коллекцию IEnumerable<R>
, пытаясь привести каждый элемент из типа T
в тип R
. В обоих случаях включается только подмножество элементов, успешно приведенных к целевому типу. Исключения не выдаются.Хотя LINQ в первую очередь реализован как библиотека для .NET Framework 3.5, он также определяет необязательные расширения языка, которые делают запросы первоклассной языковой конструкцией и предоставляют синтаксический сахар для написания запросов. Эти расширения языка изначально были реализованы в C# 3.0, [5] : 75 VB 9.0 , F# [6] и Oxygene , а другие языки, такие как Nemerle, объявили о предварительной поддержке. Расширения языка включают: [7]
var
ключевым словом . В VB9.0 Dim
ключевое слово без объявления типа выполняет то же самое. Такие объекты по-прежнему строго типизированы ; для этих объектов компилятор выводит типы переменных через вывод типа , что позволяет указывать и определять результаты запросов без объявления типа промежуточных переменных.Например, в запросе на выборку всех объектов в коллекции, количество которых SomeProperty
меньше 10,
var results = from c in SomeCollection where c.SomeProperty < 10 select new { c.SomeProperty , c.OtherProperty } ; foreach ( var result in results ) { Console.WriteLine ( result ) ; }
типы переменных result , c и results выводятся компилятором в соответствии с сигнатурами методов, которые в конечном итоге используются. Основой для выбора методов является запрос expression-free translation result
var results = SomeCollection.Where ( c = > c.SomeProperty < 10 ) .Select ( c = > new { c.SomeProperty , c.OtherProperty } ) ; результаты.ForEach ( x = > { Console.WriteLine ( x.ToString ( ) ) ; } )
Спецификация C#3.0 определяет шаблон выражения запроса вместе с правилами перевода из выражения LINQ в выражение в подмножестве C# 3.0 без выражений LINQ. Перевод, определенный таким образом, фактически не типизирован, что, в дополнение к тому, что лямбда-выражения могут интерпретироваться как делегаты или деревья выражений, обеспечивает большую степень гибкости для библиотек, желающих представить части своего интерфейса как предложения выражений LINQ. Например, LINQ to Objects работает с IEnumerable<T>
s и с делегатами, тогда как LINQ to SQL использует деревья выражений.
Деревья выражений лежат в основе механизма расширяемости LINQ, с помощью которого LINQ может быть адаптирован для многих источников данных. Деревья выражений передаются поставщикам LINQ, которые являются реализациями, специфичными для источника данных, которые адаптируют запросы LINQ для использования с источником данных. Если они выбирают это, поставщики LINQ анализируют деревья выражений, содержащиеся в запросе, чтобы сгенерировать основные части, необходимые для выполнения запроса. Это могут быть фрагменты SQL или любое другое совершенно иное представление кода в качестве дополнительных манипулируемых данных. LINQ поставляется с поставщиками LINQ для коллекций объектов в памяти, баз данных Microsoft SQL Server , наборов данных ADO.NET и XML-документов. Эти различные поставщики определяют различные разновидности LINQ:
Поставщик LINQ to Objects используется для коллекций в памяти, используя локальный механизм выполнения запросов LINQ. Код, сгенерированный этим поставщиком, ссылается на реализацию стандартных операторов запросов, определенных в Sequence
шаблоне, и позволяет IEnumerable<T>
запрашивать коллекции локально. Текущая реализация LINQ to Objects выполняет проверки реализации интерфейса, чтобы обеспечить быстрые тесты членства, подсчеты и операции индексированного поиска, когда они поддерживаются типом среды выполнения IEnumerable. [8] [9] [10]
Поставщик LINQ to XML преобразует XML-документ в коллекцию XElement
объектов, которые затем запрашиваются с использованием локального механизма выполнения, который предоставляется как часть реализации стандартного оператора запроса. [11]
Поставщик LINQ to SQL позволяет использовать LINQ для запросов к базам данных Microsoft SQL Server , включая базы данных SQL Server Compact . Поскольку данные SQL Server могут находиться на удаленном сервере, и поскольку SQL Server имеет собственный механизм запросов, LINQ to SQL не использует механизм запросов LINQ. Вместо этого он преобразует запрос LINQ в запрос SQL , который затем отправляется на SQL Server для обработки. [12] Однако, поскольку SQL Server хранит данные как реляционные данные , а LINQ работает с данными, инкапсулированными в объекты, два представления должны быть сопоставлены друг с другом. По этой причине LINQ to SQL также определяет структуру сопоставления. Сопоставление выполняется путем определения классов, которые соответствуют таблицам в базе данных и содержат все или подмножество столбцов в таблице в качестве элементов данных. [13] Соответствие, наряду с другими атрибутами реляционной модели , такими как первичные ключи , указывается с помощью атрибутов, определенных LINQ to SQL . Например,
[Таблица(Имя="Клиенты")] public class Customer { [Столбец(IsPrimaryKey = true)] public int CustID ; [Столбец] публичная строка CustName ; }
Это определение класса сопоставляется с таблицей с именем Customers
, а два члена данных соответствуют двум столбцам. Классы должны быть определены до того, как можно будет использовать LINQ to SQL. Visual Studio 2008 включает конструктор сопоставлений, который можно использовать для создания сопоставления между схемами данных в объекте, а также в реляционном домене. Он может автоматически создавать соответствующие классы из схемы базы данных , а также разрешать ручное редактирование для создания другого представления, используя только подмножество таблиц или столбцов в таблице. [13]
Отображение реализуется с помощью , DataContext
который принимает строку подключения к серверу и может использоваться для генерации , Table<T>
где T — тип, к которому будет сопоставлена таблица базы данных. Table<T>
Инкапсулирует данные в таблице и реализует IQueryable<T>
интерфейс, так что создается дерево выражений, которое обрабатывает поставщик LINQ to SQL. Он преобразует запрос в T-SQL и извлекает набор результатов с сервера базы данных. Поскольку обработка происходит на сервере базы данных, локальные методы, которые не определены как часть лямбда-выражений, представляющих предикаты, не могут использоваться. Однако он может использовать хранимые процедуры на сервере. Любые изменения в наборе результатов отслеживаются и могут быть отправлены обратно на сервер базы данных. [13]
Поскольку поставщик LINQ to SQL (выше) работает только с базами данных Microsoft SQL Server , для поддержки любой общей базы данных LINQ также включает LINQ to DataSets. Он использует ADO.NET для управления связью с базой данных. После того, как данные находятся в ADO.NET Datasets, LINQ to DataSets выполняет запросы к этим наборам данных. [14]
Непрофессиональные пользователи могут столкнуться с тонкостями в функциях и синтаксисе LINQ to Objects . Наивные шаблоны реализации LINQ могут привести к катастрофическому снижению производительности. [15] [16]
Производительность LINQ to XML и LINQ to SQL по сравнению с ADO.NET зависит от варианта использования. [17] [18]
Версия 4 .NET Framework включает PLINQ , или Parallel LINQ , параллельный механизм выполнения для запросов LINQ. Он определяет ParallelQuery<T>
класс. Любая реализация интерфейса IEnumerable<T>
может воспользоваться преимуществами механизма PLINQ, вызвав AsParallel<T>(this IEnumerable<T>)
метод расширения, определенный классом ParallelEnumerable в пространстве имен System.Linq .NET Framework. [19] Механизм PLINQ может выполнять части запроса одновременно в нескольких потоках, обеспечивая более быстрые результаты. [20]
Многие концепции, представленные LINQ, были первоначально протестированы в исследовательском проекте Microsoft Cω , ранее известном под кодовыми названиями X# (X Sharp) и Xen . Он был переименован в Cω после того, как в него был интегрирован Polyphonic C# (еще один исследовательский язык, основанный на принципах исчисления соединений ).
Cω пытается сделать хранилища данных (такие как базы данных и XML- документы) доступными с той же легкостью и безопасностью типов , что и традиционные типы, такие как строки и массивы . Многие из этих идей были унаследованы из более раннего инкубационного проекта в рамках команды WebData XML под названием X# и Xen. Cω также включает новые конструкции для поддержки параллельного программирования ; эти функции были в значительной степени получены из более раннего проекта Polyphonic C#. [21]
Впервые появившись в 2004 году в качестве предварительной версии компилятора, функции Cω впоследствии были использованы корпорацией Microsoft при создании функций LINQ, выпущенных в 2007 году в .NET версии 3.5 [22]. Конструкции параллелизма также были выпущены в слегка измененном виде в виде библиотеки под названием Joins Concurrency Library для C# и других языков .NET корпорацией Microsoft Research . [23]
Существуют порты LINQ для PHP (PHPLinq), JavaScript (linq.js), TypeScript (linq.ts) и ActionScript (ActionLinq), хотя ни один из них не является строго эквивалентом LINQ в языках, вдохновленных .NET, C#, F# и VB.NET (где он является частью языка, а не внешней библиотекой, и где он часто решает более широкий круг задач). [ необходима ссылка ]
Хотя верно, что LINQ мощный и очень эффективный, большие наборы данных все еще могут вызывать неожиданные проблемы с производительностью.
При многократном вызове запроса с Entity Framework рекомендуемый подход — использовать скомпилированные запросы LINQ. Компиляция запроса приводит к снижению производительности при первом использовании запроса, но последующие вызовы выполняются намного быстрее.