Статьи

Доступ до даних SQL Server за допомогою LINQ

Завдяки мові запитів Language Integrated Query (LINQ), реалізованому в Microsoft Visual Studio 2008, розробники можуть використовувати власний синтаксис в поєднанні з традиційними мовами програмування, такими як C # і Visual Basic (VB), для посилання на об'єкти бази даних як на власні об'єкти мови і створення запитів до цих об'єктів. Запити Visual Studio 2008 і LINQ перетворять код процедур в виклики бази даних на основі T-SQL, що направляються в SQL Server. LINQ - відмінний інструмент для швидкої розробки додатків, за допомогою якого можна формувати досить ефективні запити, хоча в багатьох випадках кваліфікований адміністратор бази даних може оптимізувати їх. На сьогодні існує три версії LINQ: LINQ for SQL, LINQ for XML і LINQ for Objects. У даній статті розглядається LINQ for SQL, іменований просто LINQ.

Як нова технологія .NET, LINQ є частиною бібліотек Microsoft .NET Framework 3.5. Для використання LINQ в проектах Visual Studio 2008 треба розташовувати платформою .NET Framework 3.5. У LINQ є ряд особливих вимог до типам даних, які не підтримуються попередніми версіями .NET, що змусило розробників Microsoft внести істотні зміни в основні мови .NET. Наприклад, в C # з'явився тип var, а в VB - відповідні типи. За допомогою цих типів об'єктів .NET можна вказати такий тип, при якому тип даних результату запиту невідомий до виконання запиту.

основи LINQ

Коротко розповім про принципи дії LINQ. Розробникам Visual Studio 2008 Надані інтегрований інструментарій для технологій доступу до даних в SQL Server 2005. Зверніть увагу, що компанія Microsoft не має випустила пакет оновлень SQL Server 2005 для підтримки NET Framework 3.5 і LINQ, так як у внутрішніх механізмах LINQ використовуються ADO. NET і існуючі методи доступу до даних, реалізовані в .NET Framework 2.0.

У LINQ об'єктна модель являє джерело даних. Потім LINQ посилається на це джерело даних як на об'єкт DataContext (наприклад, System.Data.Linq.DataContext). Об'єкт DataContext инкапсулирует рядок з'єднання ADO. NET для бази даних. Потім об'єкт DataContext використовується з набором визначень об'єкта (наприклад, System.Data.Linq.Mapping.TableAttribute) для таблиць, збережених процедур і функцій в базі даних. Для кожного об'єкта бази даних, якому дано визначення, потрібно об'єкт DataContext.

Створення класів з використанням SQLMetal

Існує два варіанти створення класів, необхідних для використання LINQ в початковому тексті прикладних програм. Можна вручну ввести всі необхідні класи за допомогою функції об'єктно-реляційного зіставлення Visual Studio. Інший спосіб - застосувати інструменти типізованих наборів даних для управління доступом і витяганням даних, а потім використовувати LINQ для запиту наборів результатів, створених типізований наборами даних. Звичайно, при використанні об'єктно-реляційного зіставлення зв'язку між таблицями в базі даних і створюваними об'єктами можуть бути тільки 1: 1. Як показує метод Load, зв'язку 1: 1 не завжди задовольняють вимогам розробників. Тому в даній статті буде розказано про те, як використовувати SQLMetal для формування класів сутностей для об'єктів бази даних.

SQLMetal.exe - безкоштовна утиліта зіставлення бази даних, що поставляється разом з Visual Studio 2008. Цей інструмент командного рядка знаходиться в каталозі Program FilesMicrosoft SDKsWindowsV6.0ain. SQLMetal формує необхідну сутність даних і об'єкт DataContext для LINQ в вихідному файлі .vb або .cs. Щоб створити вихідні файли VB для бази даних на локальному SQL Server і включити в них збережені процедури, потрібно відкрити командне вікно, перейти в каталог установки (або дати посилання на інструмент SQLMetal в цьому каталозі) і запустити наступну команду:

SQLMetal.exe / server: .SQLEXPRESS
/ database: AdventureWorks /
sprocs / functions / language: vb /
code: AdventureWorks.vb

Ця команда створює файл AdventureWorks.vb в поточному каталозі. Зверніть увагу, що потрібно змінити посилання сервера, так як .SQLEXPRESS посилається на екземпляр SQL Express на моєму локальному сервері. Цей параметр повинен містити посилання для локального комп'ютера або ім'я сервера бази даних. Параметр database: AdventureWorks вказує оброблювану базу даних. Параметри sprocs і function вказують, що SQLMetal повинен сформувати файли сутностей для підтримки збережених процедур і функцій в базі даних. Параметри language: vb і code: AdventureWorks.vb вказують відповідно мову програмування і цільової вихідний файл. Щоб створити файл AdventureWorks в поточному каталозі на мові C #, необхідно замінити параметр language: vb на language: cs, а розширення vb на cs. Після того як ця команда була виконана на тестовому комп'ютері, отриманий файл містив близько 20 тис. Рядків вихідного тексту.

Використання LINQ

Після того як сформовані суті бази даних, файл AdventureWorks.vb можна додати в будь-який проект. Для цього потрібно підготувати новий проект VB Windows Forms. Клацніть на проекті правою кнопкою миші в Solution Explorer і на пункті Add Existing Item контекстного меню, щоб відкрити діалогове вікно Add Existing Item, за допомогою якого можна перейти до файлу AdventureWorks.vb. Потім додайте цей файл до проекту.

Після того як файл AdventureWorks.vb доданий до проекту, фоновий компілятор VB видає сотні помилок, так як Visual Studio 2008 не вводить посилання, необхідні для розбору LINQ в SQL. Щоб позбутися від помилок, перейдіть до властивостей проекту, клацніть на вкладці References і додайте System.Data.LINQ в якості посилання на проект. Потім клацніть на вкладці Load Web Settings і додайте параметр рядка з'єднання AdvWorksDB для бази даних. У цьому параметрі рядка з'єднання потрібно використовувати ті ж значення, що і в SQLMetal для створення вихідного файлу. Зверніть увагу, що потрібно вказати режим безпеки (наприклад, Integrated Security) в параметрі рядка з'єднання.

Потім слід підготувати просту форму. В даному прикладі елемент керування DataGridView поміщений у верхній частині форми, а нижче розташовані чотири кнопки команд. Розташування кнопок не має великого значення, але їх потрібно назвати ButtonLoad, ButtonAdd, ButtonEdit і ButtonDelete. Двічі клацніть на кожній кнопці конструктора, щоб Visual Studio 2008 автоматично сформував обробник подій Click для кожної кнопки.

Потім потрібно двічі клацнути в області конструктора форми, щоб підготувати подія Load для форми. Потім змініть подія Load за допомогою оригінального тексту, показаного в лістингу 1.

Потім змініть подія Load за допомогою оригінального тексту, показаного в лістингу 1

Фрагмент A в лістингу 1 показує визначення об'єкта DataContext з ім'ям AdvWorksDC, який використовує рядок з'єднання бази даних, певну раніше. Цей об'єкт DataContext визначено в області форми, яка забезпечує повторне використання об'єкта DataContext в обробниках подій в формі. Другий рядок вихідного тексту у фрагменті A (лістинг 1) визначає об'єкт суті (т. Е. Department) для таблиці HumanResources_Department з бази даних AdventureWorks, яка також використовується в декількох обробниках подій.

Оригінальний текст у фрагменті B лістингу 1 визначає подія Form Load для показу. У події Load у фрагменті B лістингу 1 відключаються кнопки Add, Edit і Delete. Подія Load відбувається тільки один раз, тому настає зручний момент, щоб створити початковий запит даних для заповнення таблиці. Замість простого запиту, який можна легко додати, оновити або видалити, цей складний запит краще ілюструє формат запитів LINQ. Відмінність запитів LINQ від запитів SQL полягають в тому, що вони починаються з пропозиції FROM. Ця пропозиція дозволяє задати цільову таблицю в оперативній пам'яті, в якій міститься визначення запиту. У розділі In пропозиції FROM вказується, де в об'єкті DataContext буде зроблений запит. Після того як визначено контекст запиту, механізм LINQ може надати технологію IntelliSense для таблиць і стовпців, доступних в запиті.

Запит у фрагменті B лістингу 1 використовує інструкцію Join, яка називає другу таблицю і вказує, які стовпці будуть використовуватися для з'єднання двох таблиць. Запит також містить пропозицію WHERE, щоб обмежити число що повертаються результатів. Результат - об'єкт DS, створений як об'єкт запиту на основі типу інтерфейсу. Цей об'єкт запиту обслуговує базовий запит і надає нумератори, що дозволяє витягти кожен рядок результатів. Потім об'єкт запиту призначається як джерело даних для таблиці даних. Результуюча таблиця (показана на екрані 1) не забезпечує редагування або додавання елементів на етапі виконання. Потрібно створити функціональний еквівалент інструкції T-SQL, в якому результати призначаються набору даних.

Потрібно створити функціональний еквівалент інструкції T-SQL, в якому результати призначаються набору даних

Додавання рядків в таблицю

Тепер можна скомпілювати і запустити додаток, яке виконає запит, визначений у події Load. Ці дані завантажуються в таблицю даних. Однак запит посилається на таблицю з декількома зв'язками. Щоб проілюструвати операції Insert, Update і Delete, буде використаний запит, який посилається на таблицю без зв'язків.

У лістингу 2 показана реалізація трьох ключових методів процесу: ButtonLoad_Click, Bind- Grid і ButtonAdd_Click. Метод ButtonLoad_Click перезавантажує таблицю з іншою таблицею (т. Е. Таблицею HumanResources_Department). Метод BindGrid створює власне запит LINQ, спрямований до об'єкта DataContext для записів в таблиці, а потім оновлює джерело даних DataGridView за допомогою нового запиту. Запит демонструє пропозицію ORDER BY, знайоме розробникам T-SQL. Нарешті, метод ButtonAdd_Click активізує кнопку Add (див. Екран 2).

Екран 2)

Кнопка Add викликає метод ButtonAdd_Click в лістингу 2, щоб додати новий запис в поточну таблицю. Цей метод використовує об'єкт суті, створений як частина визначення форми в лістингу 1. Потім об'єкт суті асоціюється з новим створеним екземпляром підрозділи, як показано в першому рядку методу ButtonAdd_Click в лістингу 2. У цьому рядку вихідного тексту використовується один з нових синтаксичних елементів VB, який задає значення властивостей об'єкта при його створенні. Синтаксис With {.PropertyName = value} дозволяє задавати значення властивостей об'єкта при його створенні з використанням інструкції New. У рядку показано створення нової сутності Department, яка буде відображати рядок, яка оновлюється в базі даних.

Потім потрібно вставити нову сутність підрозділи в таблицю. Оновлення таблиці - двоетапний процес: по-перше, потрібно зв'язати новий об'єкт підрозділу зі списком поставлених в чергу інструкцій вставки об'єкта бази даних за допомогою методу InsertOnUpdate. Цей метод вказує LINQ, що об'єкт буде вставлений в таблицю, і дозволяє визначити кілька нових об'єктів перед оновленням бази даних. При використанні LINQ ці та інші оновлення залишаються локальними, поки виконується метод SubmitChanges. По-друге, метод SubmitChanges забезпечує отримання кешованих оновлень даних і застосування сформованого програмного коду T-SQL, пов'язаного з кожним з них, до джерела даних SQL. Якщо потрібно додати колекцію сутностей, можна відкласти оновлення бази даних до тих пір, поки не будуть створені всі сутності. Після клацання на кнопці Add дисплей оновлюється, і новостворене підрозділ з'являється на початку списку, показаного на екрані 3.

Оновлення та видалення рядків

Після того як рядок вставлена, розглянемо, як її оновити. Оскільки об'єкт суті був створений з поточного об'єкта DataContext, потрібно лише оновити одне з властивостей об'єкта суті, вказавши значення, яке потрібно помістити в базу даних. У нашому прикладі назва підрозділу зміниться з Bike Computers на Fitness Computers.

Для оновлення бази даних викличте метод SubmitChanges об'єкта DataContext. На екрані 4 показано, як назва підрозділу змінюється з Bike Computers на Fitness Computers після виконання методу SubmitChanges. Нова назва підрозділу замінює початкове. Тепер доступна тільки кнопка Delete.

Теоретично видалити рядок так само легко, як оновити; однак внутрішній механізм складніше. За замовчуванням LINQ використовує оптимістичну схему блокування. Якщо LINQ вважає, що базові дані об'єкта змінилися, то об'єкт не буде оновлений і повертається повідомлення про помилку з зазначенням, що рядок не знайдена або змінилася. Це повідомлення про помилку буде відображатися також при багаторазовому редагуванні суті або спробі видалити сутність після редагування.

Одне з рішень цієї проблеми - замінити екземпляр об'єкта DataContext новим екземпляром DataContext. Перші два рядки обробника подій ButtonDelete_Click (див. Лістинг 3) відтворюють об'єкт DataContext і пов'язують об'єкт entity в оперативній пам'яті з новоствореним об'єктом DataContext. Але це рішення може вплинути на продуктивність, так як вимагає створити нове з'єднання з базою даних.

Але це рішення може вплинути на продуктивність, так як вимагає створити нове з'єднання з базою даних

Після того як отриманий виклик для поновлення об'єкта DataContext на місці, викликається метод DeleteOnSubmit, щоб видалити поточну сутність з таблиці, а потім передати зміни для дійсного видалення рядка суті з таблиці. Після клацання на кнопці Delete знову з'являється картина, подібна показаної на екрані 2, і процес можна повторити. Зверніть увагу, що метод DeleteOnSubmit замінює метод Remove з попередніх версій LINQ.

Погляд розробників на LINQ

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

Ці два подання підсумовують оцінку LINQ. Як інструмент RAD, LINQ - потужне доповнення до арсеналу розробника .NET. Розширюваний інтерфейс автоматизує потенційно складні запити. Однак для професіонала, який бажає оптимізувати доступ до даних, LINQ навряд чи вплине на виконання повсякденних завдань.

Вільям Шелдон ( [email protected] ) - провідний інженер компанії InterKnowlogy, має сертифікати MCSD і MCP + SiteBuilding

Новости