- 10.1. Доступ до даних в .NET
- 10.1.1. LINQ
- 10.1.1.2. Введення в запити LINQ
- 10.1.1.2.1. Джерело даних
- 10.1.1.2.2. запит
- 10.1.1.2.3. виконання запиту
Анотація: У даній лекції дається введення в технології доступу до даних Linq, Linq to SQL, ADO .NET Entity Framework і технологію Data Driven Development (DDD).
Презентацію до даної лекції Ви можете завантажити тут .
10.1. Доступ до даних в .NET
Продовжимо розгляд технологій. NET для доступу до даних. У даній лекції розглянемо технології LINQ і LINQ to SQL, а також ADO .NET Entity Framework.
10.1.1. LINQ
10.1.1.1. Загальні відомості
Language Integrated Query (LINQ) - проект Microsoft по додаванню синтаксису мови запитів, що нагадує SQL, в мови програмування платформи .NET Framework [ 1 ]. LINQ випущений разом з Visual Studio 2008 року в кінці листопада 2007 року.
LINQ (Language-Integrated Query) являє собою набір функцій Visual Studio 2008, що розширюють потужні можливості запиту в синтаксисі мови C # і Visual Basic. LINQ являє стандартні шаблони для створення запитів та оновлення даних; технологія може бути розширена для підтримки потенційно будь-якого типу сховища даних [ 2 ]. Visual Studio 2008 включає збирання постачальників LINQ, що дозволяють використовувати LINQ з колекціями платформи .NET Framework, базами даних SQL Server, наборами даних ADO.NET і XML-документами.
LINQ являє собою набір розширень мови, що підтримує формування запитів даних способом, безпечним за типами. Запитувані дані можуть бути представлені у формі XML (запити LINQ до XML), баз даних (ADO.NET з підтримкою LINQ, куди входять LINQ до SQL, LINQ до наборів даних і LINQ до примірників), об'єктів (LINQ до об'єктів) і т. д. Архітектура LINQ показана на Мал. 10.1 [ 3 ].
Мал.10.1.
архітектура LINQ
Розглянемо приклад інтегрованого запиту [ 3 ]:
var contacts = from c in customers where c.City == "Москва" select new {c.Name, c.Phone};
Тут з деякого джерела даних customers вибираються імена і телефони клієнтів, які проживають в місті Москва. Запит дуже схожий на мову SQL, проте використовує строго типізований синтаксис.
Цей же запит можна записати і в іншому вигляді - використовуючи лямбда-вирази і методи розширення ( Мал. 10.2 ). Саме до такого виду призведе код, наведений у прикладі вище, компілятор.
Мал.10.2.
Два способи написання запиту LINQ на мові C # 3.0
Використовуючи деякі нові особливості мови, LINQ дозволяє використовувати SQL-подібний синтаксис безпосередньо в коді програми, написаної, наприклад, на мові C #:
- Анонімні типи;
- Методи розширення;
- Лямбда-числення;
- Дерево виразів;
- Стандартні оператори мови запитів.
Розглянемо докладніше основні принципи роботи з LINQ на C # [ 2 , 3 , 4 , 5 , 6 , 7 , 8 ].
10.1.1.2. Введення в запити LINQ
Примітка .В деяких прикладах далі використовується база даних "Northwind". Її можна завантажити в Центрі завантажень Microsoft.
LINQ пропонує узгоджену модель для роботи з даними в різних видах джерел даних і в різних форматах. У запиті LINQ робота завжди здійснюється з об'єктами. Для запитів і перетворень даних в XML-документах, базах даних SQL, наборах даних ADO.NET, колекціях .NET і будь-яких інших форматах, для яких доступний постачальник LINQ, використовуються однакові базові шаблони кодування.
Всі операції запиту LINQ складаються з трьох різних дій.
- отримання джерела даних;
- створення запиту;
- виконання запиту.
У наступному прикладі показано вираз цих трьох частин операції запиту в вихідному коді. У прикладі в якості джерела даних для зручності використовується масив цілих чисел; тим не менш, ті ж принципи застосовні і до інших джерел даних. Частина, що залишилася розділу посилається на цей приклад:
class IntroToLINQ {static void Main () {// 1. Отримання джерела даних int [] numbers = new int [7] {0, 1, 2, 3, 4, 5, 6}; // 2. Створення запиту // numQuery - це IEnumerable <int> var numQuery = from num in numbers where (num% 2) == 0 select num; // 3. Виконання запиту foreach (int num in numQuery) {Console.Write ( "{0,1}", num); }}}
на Мал. 10.3 показана завершена операція запиту. У LINQ виконання запиту відрізняється від самого запиту; іншими словами, створення змінної запиту само по собі не пов'язане з отриманням даних.
Мал.10.3.
Операція запиту в LINQ
Джерело: Введення в запити LINQ [ 2 ]
Розглянемо докладніше дії в LINQ.
10.1.1.2.1. Джерело даних
У попередньому прикладі джерелом даних є масив, тому він неявно підтримує універсальний інтерфейс IEnumerable <(Of <(T>)>). Це означає, що до нього можна виконувати запити з LINQ. Запит виконується в операторі foreach, і оператору foreach потрібно інтерфейс IEnumerable або IEnumerable <(Of <(T>)>). Типи, які підтримують IEnumerable <(Of <(T>)>) або похідні інтерфейси, такі як універсальний інтерфейс IQueryable <(Of <(T>)>), називаються затребуваними типами.
Для запитуваної типу, який виступає в якості джерела даних LINQ, не потрібні зміни або спеціальна обробка. Якщо джерело даних ще не знаходиться в пам'яті у вигляді запитуваної типу, постачальник LINQ повинен представити його як такої. Наприклад, LINQ to XML завантажує XML-документ в запитуваний тип XElement:
// Створення джерела даних з XML-документа, використовуючи System.Xml.Linq XElement contacts = XElement.Load (@ "c: \ myContactList.xml");
Використовуючи LINQ to SQL, спочатку вручну або за допомогою Реляційний конструктор об'єктів створюється об'єктно-реляційне зіставлення в режимі розробки. Потім можна написати запити до об'єктів, а під час виконання LINQ to SQL буде здійснювати взаємодію з базою даних. У наступному прикладі Customer представляє певну таблицю в базі даних, а Table <Customer> підтримує універсальний IQueryable <(Of <(T>)>), похідний від IEnumerable <(Of <(T>)>):
// Створення джерела даних з бази даних SQL Server, використовуючи System.Data.Linq DataContext db = new DataContext (@ "c: \ northwind \ northwnd.mdf");
10.1.1.2.2. запит
Запит вказує, яку інформацію потрібно витягти з джерела або джерел даних. При необхідності, запит також вказує спосіб сортування, угруповання і формування цих відомостей перед поверненням. Запит зберігається в змінної запиту і инициализируется виразом запиту. Щоб спростити написання запитів, в C # з'явився новий синтаксис запиту.
Запит з попереднього прикладу повертає всі парні числа з масиву цілих чисел. Вираз запиту містить три пропозиції: from, where і select. Пропозиція from вказує джерело даних, пропозиція where застосовує фільтр, а пропозиція select вказує тип повертаються елементів. Важливо, що в LINQ сама змінна запиту не вживає жодних дій і не повертає ніяких даних. Вона просто зберігає відомості, необхідні для надання результатів при подальшому виконанні запиту.
10.1.1.2.3. виконання запиту
Виконання запиту поділяють на відкладене і примусове (негайне).
Як вже говорилося раніше, сама змінна запиту тільки зберігає команди запиту. Фактичне виконання запиту відкладається до виконання ітерації змінної запиту в операторі foreach. Цю концепцію називають відкладеним виконанням, вона показана в наступному прикладі:
// Виконання запиту foreach (int num in numQuery) {Console.Write ( "{0,1}", num); }
Оператор foreach є також місцем, де беруться результати запиту. Наприклад, в попередньому запиті змінна ітерації num містить кожне (по черзі) значення в яку повертатимуть послідовності.
Запити, які виконують статистичні функції над діапазоном вихідних елементів, повинні спочатку виконати ітерацію цих елементів. Прикладами таких запитів є Count, Max, Average і First. Вони виконуються без явного оператора foreach, оскільки сам запит повинен використовувати foreach для повернення результату. Зверніть увагу, що такий тип запитів повертає одиночне значення, а не колекцію IEnumerable. Наступний запит повертає кількість парних чисел в початковому масиві:
var evenNumQuery = from num in numbers where (num% 2) == 0 select num; int evenNumCount = evenNumQuery.Count ();
Щоб примусово викликати негайне виконання будь-якого запиту і кешувати його результати, можна викликати метод ToList <(Of <(TSource>)>) або ToArray <(Of <(TSource>)>):
List <int> numQuery2 = (from num in numbers where (num% 2) == 0 select num) .ToList (); // або так: // numQuery3 - це як і раніше int [] var numQuery3 = (from num in numbers where (num% 2) == 0 select num) .ToArray ();
Можна також примусово виконати запит, помістивши цикл foreach відразу після висловлення запиту. Однак виклик ToList або ToArray також кешируєт всі дані в одній колекції об'єктів.