Відкрити головне меню

LINQ (англ. Language Integrated Query - запити, інтегровані в мову) - компонент Microsoft .NET Framework, який додає нативні можливості виконання запитів даних до мов, що входять у .NET. Хоча порти існують для PHP (PHPLinq), JavaScript(linq.js), TypeScript (linq.ts), і ActionScript (ActionLinq), - жоден з них не є абсолютно еквівалентним LINQ в C# (де LINQ - не просто додаткова бібліотека, а частина мови).

LINQ розширює можливості мови, додаючи до неї вирази запитів, що є схожими на твердження SQL та можуть бути використані для зручного отримання та обробки даних масивів, XML документів, реляційних баз даних та сторонніх джерел. LINQ також визначає набір імен методів (що називаються стандартними операторами запитів, або стандартними операторами послідовностей), а також правила перекладу, що має використовувати компілятор для перекладу текучих виразів у звичайні, використовуючи їх назву, лямбда-вирази та анонімні типи.

Багато концептів, що представлені у LINQ, були спершу випробувані у дослідницькому проекті Microsoft . LINQ був випущений як частина .NET Framework 3.5 19 листопада 2007 року.

Зміст

Архітектура LINQ в .NET FrameworkРедагувати

Стандартні оператори запитівРедагувати

Далі описано роботу операторів з колекціями. Багато операторів приймають у якості аргументу функції. Ці функції можуть бути представлені як у вигляді іменованого методу, так і анонімної функції.

Набір операторів запитів, що визначені в LINQ, надається користувачу у вигляді API. Оператори запитів, що підтримуються API[1]:

Select

Даний оператор виконує проекцію на колекцію, щоб отримати необхідні члени елементів. Користувач передає у якості параметру довільну функцію у вигляді лямбда-виразу, що проектує члени даного об'єкту. Функція передається до оператора у вигляді делегату.

Where

Оператор Where дозволяє визначити набір предикатів, що виконуються над кожним елементом колекції та відкидає елементи, що не підпадають під задану умову з результуючого набору. Предикат передається до оператора у вигляді делегату.

SelectMany

SelectMany використовується для створення вихідної колекції згідно заданої користувачем проекції з вхідних колекцій.

Sum / Min / Max / Average

Дані оператори можуть приймаюти функцію, яка співставляє кожне значення колекції з певним числом, після чого можна знайти їх суму, мінімальне, максимальне, або середнє значення. Перевантажені версії даних методів не приймають функцій на вхід, а виконують відповідні дії над самими елементами колекції.

Aggregate

Узагальнений оператор Sum / Min / Max. Він приймає у якості аргументу функцію, що вказує яким чином треба поєднати два елементи, щоб утворити проміжний, або кінцевий результат. Додатково можна задати початкове значення. Крім цього, можна задати завершальну функцію, що перетворить результат агрегації на фінальне значення.

Join / GroupJoin

Оператор Join виконує inner join щодо двох колекцій, орієнтуючись на співпадаючі ключі для об'єктів з обидвох колекцій. Він приймає дві функції у вигляді делегатів, по одній для кожної колекції, а потім застосовує їх до кожного елемента відповідної колекції, щоб отримати їх ключі. Він також отримує інший делегат, у якому користувач вказує які члени вхідних об'єктів повинні міститися у результуючому наборі. Оператор GroupJoin виконує group join. Так як і оператор Select, результати з'єднання є екземплярами класу, чиї поля співпадають з полями вхідних об'єктів, або є їх підммножиною.

Take / TakeWhile

Оператор Take повертає перші n елементів колекції, тоді як TakeWhile повертає не більше n перших послідовних елементів, що підпадають під умови предикату.

Skip / SkipWhile

Аналогічно до Take та TakeWhile, дані оператори відповідно пропускають перші n елементів, або не більше n перших послідовних елементів, що відповідають предикату.

OfType

Використовується для отримання елементів певного типу.

Concat

Даний оператор конкатенує дві колекції.

OrderBy / ThenBy

Оператор OrderBy дозволяє задати порядок сортування елементів в колекції по певному ключу. За умовчанням сортування відбувається у порядку зростання. Для сортування по спаданню, треба використовувати оператор OrderByDescending. ThenBy and ThenByDescending дозволяють задати правила сортування підпослідовностей. Сортування відбувається по ключу, що користувач передав в якості делегату.

Reverse

Даний оператор повертає елементи колекції в зворотньому порядку.

GroupBy

Оператор GroupBy приймає у якості параметру функцію, що по певному ключу повертає колекцію об'єктів IGrouping<Key, Values> для кожного унікального значення ключа. Об'єкти IGrouping можна використати для перечислення всіх елементів з певним ключем.

Distinct

Оператор вилучає дублікати елементів з колекціїї. Перевантажена версія оператору приймає у якості аргументу об'єкт, що здійснює перевірку елементів на рівність.

Union / Intersect / Except

Дані оператори використовуються, щоб виконати операції об'єднання, перетину та різниці над двома послідовностями. Кожен з них має перевантажену версію, що приймає об'єкт, який перевіряє на рівність елементи колекцій.

SequenceEqual

SequenceEqual визначає чи усі елементи колекцій, що знаходяться на однаковій позиції, є рівними.

First / FirstOrDefault / Last / LastOrDefault

Дані оператори отримують предикат у якості аргументу. Оператор First повертає перший елемент колекції, що підпадає під умову. Якщо ж такого елемента не існує, викидається виключення. Оператор FirstOrDefault діє так само як First, але замість виключення повертає значення за умовчанням відповідного типу. Оператори Last та LastOrDefault виконують аналогічні операції, але з останніми підходящими елементами у колекції.

Single / SingleOrDefault

Оператор Single отримує предикат у якості аргументу та повертає єдиний елемент, що задовольняє предикат. Якщо ж таких елементів більше, ніж один, або немає взагалі, викидається виключення. При використанні SingleOrDefault, якщо жоден елемент у колекції не відповідає предикату, то замість виключення, повертається значення типу за умовчанням. В усіх інших випадках діє так само, як і Single.

ElementAt

Даний оператор повертає елемент, що має відповідний індекс у колекції.

Any / All

Оператор Any перевіряє чи хоча б один елемент у колекції відповідає предикату та повертає відповідне булеве значення. Виклик Any без предикату поверне істину, якщо в колекції є хоча б один елемент. Оператор All перевіряє чи відповідні предикату абсолютно всі елементи колекції.

Contains

Перевіряє чи колекція містить шуканий елемент.

Count

Підраховує кількість елементів у даній колекції. Перевантажений варіант приймає предикат у якості аргументу та рахує кількість елементів, що відповідають предикату.

Стандартне API операторів запитів також надає можливість конвертації колекцій до іншого типу:

  • AsEnumerable: статично типізує колекцію як IEnumerable<T>.
  • AsQueryable: статично типізує колекцію як IQueryable<T>.
  • ToArray: створює масив типу T[] з вхідної колекції.
  • ToList: створює колекцію типу List<T> з вхідної колекції.
  • ToDictionary: створює колекцію типу Dictionary<K, T> з вхідної колекції, з індексами типу K. Користувач передає у якості аргументу функцію проекції, що повертає ключ для кожного об'єкту.
  • ToLookup: створює колекцію типу Lookup<K, T> з вхідної колекції, з індексами типу K. Користувач передає у якості аргументу функцію проекції, що повертає ключ для кожного об'єкту.
  • Cast: конвертує неузагальнену колекцію типу IEnumerable в узагальнену IEnumerable<T>. В іншому випадку - конвертує узагальнену колекцію з одним параметром типу в колекцію з іншим параметром. Наприклад, IEnumerable<T> в IEnumerable<R>, приводячи кожен елемент типу T до елементу типу R. В разі, якщо хоча б один елемент колекції не може бути приведений до необхідного типу, викидається виключення.
  • OfType: конвертує неузагальнену колекцію типу IEnumerable в узагальнену IEnumerable<T>. В іншому випадку - конвертує узагальнену колекцію з одним параметром типу в колекцію з іншим параметром. Наприклад, IEnumerable<T> в IEnumerable<R>, намагаючись привести кожен елемент типу T до елементу типу R. У результуючій колекції будуть знаходитися лише ті елементи, що були успішно сконвертовані.

Розширення мовиРедагувати

З появою LINQ, до мов програмування також був доданий синтаксичний цукор, щоб полегшити написання запитів. Дані розширення включають в себе такі:

  • Синтаксис запитів: розробники мови програмування мають змогу обрати будь-який зручний синтаксис. Ці ключові слова повинні перекладатися компілятором до відповідних викликів методів LINQ.
  • Неявно типізовані змінні: дане покращення дозволило оголошувати змінні без явного зазначення їх типів. Починаючи з C# 3.0 стало можливим використовувати ключове слово var. У VB9.0 для цього існує ключове слово Dim. Такі об'єкти все ще є строго типізованими.
  • Анонімні типи: анонімні типи дозволяють компілятору автоматично створювати та опрацьовувати класи, що містять лише оголошення полів. Це є зручним для таких операторів, як Select та Join, результат виконання яких часто відрізняється від вхідних типів. Компілятор використовує інтерфейси типів для визначення полів, що мають міститися у вихідному класі і генерує до них методи доступу та зміни для них.
  • Ініціалізатор об'єкту: ініціалізатори об'єктів дозволяють створювати та ініціалізувати поля об'єкту в одній області видимості, що можна використовувати в операторах Join та Select.
  • Лямбда вирази: лямбда вирази дозволяють створювати компактні предикати, або функції проекції. Вони передаються у функції у вигляді делегатів, або дерев виразів, залежно від провайдера запитів.

Наприклад, у запиті, що повертає всі об'єкти зі значенням SomeProperty меншим, ніж 10, типи змінних result та results виводяться компілятором, відповідно до сигнатур використаних методів:

var results =  from c in SomeCollection
               where c.SomeProperty < 10
               select new {c.SomeProperty, c.OtherProperty};

foreach (var result in results)
{
        Console.WriteLine(result);
}

Аналогічний запит також можна подати у наступному вигляді:

var results =
     SomeCollection
        .Where(c => c.SomeProperty < 10)
        .Select(c => new {c.SomeProperty, c.OtherProperty});

results.ForEach(x => {Console.WriteLine(x.ToString());})

Провайдери LINQРедагувати

LINQ надає можливість працювати з різними джерелами даних. Для цього існують провайдери[2]. Будь-який розробник може написати власний провайдер для LINQ[3].

LINQ to Objects[4]Редагувати

Провайдер LINQ to Objects використовується для колекцій, що знаходяться в пам'яті та застосовує для їх обробки локальний функціонал LINQ. Код, згенерований даним провайдером, реалізує архітектурний шаблон Sequence і дозволяє локально виконувати операції над колекціями IEnumerable<T>. Поточна реалізація LINQ to Objects перевіряє які інтерфейси реалізовані у типі, що входить до колекції, щоб використати їх для швидкого виконання тих, чи інших запитів, якщо вони підтримуються типом під час виконання.

LINQ to XML[5]Редагувати

Провайдер LINQ to XML конвертує XML документ в колекцію об'єктів XElement, над якою виконується запит, використовуючи локальний функціонал LINQ.

LINQ to SQL[6]Редагувати

Провайдер LINQ to SQL дозволяє виконувати запити до бази даних Microsoft SQL Server. Оскільки дані, що зберігаються у SQL Server, зазвичай знаходяться на іншому сервері та SQL Server має свій власний функціонал по виконанню запитів, LINQ to SQL не використовує локальні можливості LINQ. Замість цього, вирази LINQ перекладаються у запити на мові SQL та відправляються до SQL Server, який обробляє ці запити та повертає результат, якщо необхідно. Однак, оскільки SQL Server є реляційною базою даних, а LINQ працює з об'єктами мови програмування, необхідно співставити відповідні дані. Для цього LINQ to SQL також реалізує спеціальний фреймворк. Співставлення відбувається, оголошуючи класи, які є аналогами таблиць у базі даних і містять в собі всі атрибути таблиці, або їх підмножину. Відповідність між полями класу та атрибутами у таблиці встановлюється через атрибути. Наприклад,

[Table(Name="Customers")]
public class Customer
{
     [Column(IsPrimaryKey = true)]
     public int CustID;

     [Column]
     public string CustName;
}

Даний клас співставляється з таблицею Customers та містить в собі 2 поля, що відповідають двом колонкам таблиці.

LINQ to DataSets[7]Редагувати

Оскільки провайдер LINQ to SQL працює лише з SQL Server, для підтримки інших баз даних створено LINQ to DataSets. Даний провайдер використовує можливості ADO.NET для встановлення комунікації з базою даних. LINQ to DataSets виконує запити над даними, що містяться в наборах даних ADO.NET.

PLINQ[8]Редагувати

Починаючи з четвертої версії, .NET Framework включає в себе PLINQ (Parallel LINQ), що реалізує виконання LINQ запитів паралельно. Будь-який клас, що реалізує інтерфейс IEnumerable<T>, може скористатися перевагами PLINQ, викликавши метод розширення AsParallel<T>(this IEnumerable<T>), що визначений у класі ParallelEnumerable простору імен System.Linq. PLINQ може виконувати частини запиту одночасно в різних потоках, що дозволяє отримати результат швидше.

ПриміткиРедагувати

  1. Classification of Standard Query Operators by Manner of Execution (C#). docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  2. Introduction to LINQ (C#). docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  3. Walkthrough: Creating an IQueryable LINQ Provider. msdn.microsoft.com (en-us). Процитовано 2018-05-06. 
  4. LINQ to Objects (C#). docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  5. LINQ to XML (C#). docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  6. LINQ to SQL. docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  7. LINQ to DataSet. docs.microsoft.com (en-us). Процитовано 2018-05-06. 
  8. Parallel LINQ (PLINQ). docs.microsoft.com (en-us). Процитовано 2018-05-06. 

ПосиланняРедагувати