Книги, научные публикации Pages:     | 1 | 2 | 3 | 4 | 5 | -- [ Страница 1 ] --

том 1 альманах программиста Тематический сборник материалов Library и Magazine ADO.NET SQL Доступ к из приложений Составитель Ю. Е. Купцевич Москва 2003 fii. P У С P i К УДК 004.45 ББК

32.973.26-018.2 А57 А57 том Microsoft ADO.NET, Microsoft SQL Server, к данным Ч Издательско-торговый дом - 400 ил.

ISBN 5-7502-0234-8 Альманах представляет собой тематический сборник статей из журнала Magazine/Русская Редакция и Microsoft MSDN Library. Издание адресовано широкому кругу программистов, современными и перспективными информаци онными технологиями. Первый том альманаха, работе с базами данных, состоит из трех тематических содержащих 20 статей.

УДК 004.45 ББК 32.973.26-018.2 ActiveX, JScript, Microsoft, Microsoft Press, MSDN, Outlook. SQL Server, VBScript, Visual Basic, Visual Visual Visual Studio, Win32, Windows и Windows NT либо охраняемыми товарными знаками, либо товарными знаками корпорации Microsoft в США и/или других странах. NT Ч товар ный знак компании Limited. Все товарные знаки являются собственностью соответствующих фирм.

приведенная в этой в том числе и другие ссылки на Web-сай ты, может быть изменена без предварительного уведомления.

Microsoft Corporation и CMP Media ISBN 5-7502-0234- 0 Редакция, Альманах программиста, том 1:

Microsoft ADO.NET, Microsoft SQL доступ к данным из приложений Ю. Е.

редактор Л. А, Компьютерная верстка и дизайн В. Б.

Дизайнер обложки Е. В. Козлова Главный редактор А. И. Козлов к печати Москва, ул.

(095) тел./факс: info@rusedit.ru, в печать экз. Формат л. в Москва, ул. Фр. Энгельса, Оглавление ADO.NET Алекс Крис Брукс, Стив Басби, Эд Руководство по архитектуре доступа к данным на платформе Джонни Папа Доступ к данным ADO.NET: концепции и реализация Боб Бьючмин ADO.NET Разработка собственных провайдеров данных для Data Access Framework Джонни Папа Доступ к данным Выражения в ADO.NET Дино Эспозито На переднем крае Двоичная сериализация Прийя Дхаван транзакциями Разработка распределенных приложений в Microsoft SQL Server Джонни Папа Доступ к данным Пять способов подстегнуть производительность SQL и Дэниел Уильяме Сценарии в SQL Преобразование данных и отчетов SQL Server 2000 через VBScript-интерфейсы Балена и Автоматизация выполнения административных задач в SQL Server Энсон Голдэйд и Вэрон Фугман SQL и XML Вызов хранимых процедур и получение их результатов через Web Марк Браун и Дэвид Мобильность Компактные и надежные приложения на основе SQL Server СЕ 2.0 и Compact Framework Марк Браун Доставка информации в реальном времени с применением Notification Services Доступ к данным из приложений Азиз Reflection Динамическое связывание уровня данных с хранимыми процедурами и командами SQL Майкл Говард и Кит Браун Советы защите Десять лучших приемов защиты о которых должен знать каждый разработчик Джонни Папа Доступ к данным Объекты PataRelation в ADO.NET Прийя Разработка приложений в Операции над данными с иерархической структурой Джонни Папа Доступ к данным Модификация приложения для отображения данных в Web Джонни Папа Доступ к данным ADO и создание уровня доступа к данным на основе компонента Марк Spider в Сбор информации от Web-сайтов каталогов с применением Visual Basic и ADO.NET Кен Спенсер Основы и тонкости Управление транзакциями между компонентами.. От составителя Уважаемый читатель!

У вас в руках Ч первый том альманаха программиста, который издатель ство Русская Редакция планирует выпускать по мере накопления мате риалов (примерно раз в квартал). Это уникальное издание адресовано про фессионалам в области современных информационных технологий. Каж дый том представляет собой тематический сборник статей из журнала Magazine и Library по наиболее актуальным и перспективным технологиям разработки программного обеспечения.

Главная которую мы ставили себе при подготовке альманаха, Ч из бавить вас от поисков нужных материалов, опубликованных в отдельных номерах MSDN Magazine или документах MSDN Library за 2000-2003 гг.

Поэтому альманах, помимо статей, уже напечатанных в журнале MSDN Magazine/Русская Редакция, включает материалы, переведенные специ ально для данного издания.

Первый том альманаха, посвященный работе с базами данных, состоит из трех тематических рубрик.

Х Технология доступа к данным Microsoft ADO.NET. Подробно опи сывается архитектура доступа к данным на платформе поясня ются концепции и реализация ADO.NET, показывается, что представ ляют собой выражения (expressions) в ADO.NET, как создавать соб ственные провайдеры данных и управлять транзакциями.

Х Microsoft SQL Server. Подборка материалов за период 2001-2002 гг.

по оптимизации SQL-запросов, использованию сценариев в SQL, объ ектной модели SQL-DMO в SQL Server 7.0 и SQL Server 2000, созда нию хранимых процедур SQL Server для автоматизации доставки ин формации в XML-формате из базы данных в клиентские компонен ты, службе Notification Services, а также по новым возможностям и практическому применению SQL Server CE 2.0 для мобильных уст ройств, От Х Доступ к данным из Материалы по динамическому связыванию уровня данных с хранимыми процедурами и SQL через механизм Reflection, использованию DataRelation, операциям над иерархическими наборами строк, со зданию уровня доступа к данным через компонент (для тех, кто пока работает с сбору информации от Web-сайтов и каталогов с применением реализации универсального сред отображения данных на основе СОМ+ или (Microsoft Trans action Services), выполнению локальных и распределенных транзакций в созданию транзакций в ADO.NET.

Исходный код для статей альманаха можно скачать по ссылке, указанной в конкретной статье, или в полном комплекте (для всех статей альманаха) с издательства Русская по ссылке Второй том альманаха будет посвящен тематике, связанной с и Интернет-приложениями, в том числе использованию HTTP-конвейеров, работе с элементами управления ASP.NET, написанию безопасного кода, созданию Web-сервисов, а также разработке приложений электронной коммерции и других Web-приложений.

В дальнейшем планируется выпуск альманахов по базовым механизмам Framework (модели зашиты, отражение, удаленное взаимодействие, сервисы взаимодействия с неуправляемым кодом и т. д.), специфике ков программирования, отладке/тестированию и по другим темам.

Если вас интересует специфическая тематика или определенные матери алы из Magazine и Library, обращайтесь на сайт издатель ства www.rusedit.tu или по адресу almanah@rusedit.ru. Мы постараемся учесть ваши пожелания в будущих выпусках альманаха.

Алекс Крис Брукс, Стив Эд Джезирски Руководство по архитектуре доступа к данным на платформе В этом документе разработки на основе ADO.NET уровня доступа к данным в многоуровневом Основное внимание уделяется ряду наиболее распространенных задач и связанных с доступом к данным. Даются рекомендации по выбору наиболее подходящих методов и приемов.

Введение При разработке уровня доступа к данным следует ис пользовать модель доступа к данным Microsoft ADO.NET. ADO.NET обла дает богатыми возможностями и удовлетворяет требованиям доступа к данным, предъявляемым многоуровневыми слабосвязанными Web-прило жениями и Web-сервисами. Как и многие другие объектные модели с под богатой функциональности, ADO.NET позволяет решать одни и же задачи несколькими способами.

В этом документе содержатся рекомендации по выбору наиболее подходя щего метода доступа к данным. С этой целью детально описывается целый ряд типичных ситуаций доступа к данным, даются рекомендации по повы шению производительности и предлагаются наиболее эффективные спо собы работы. Кроме того, в документе даются ответы на другие часто за даваемые где лучше хранить строки подключения к базам данных?

Как реализовать поддержку пула соединений (connection pooling)? Как ра ботать с транзакциями? Как загружать данные постранично (paging), чтобы пользователи могли пролистывать наборы записей большого объема?

* Alex Chris Brooks, Steve Busby, and Ed Access Architecture Library. Microsoft Corporation. 2001. October. - Прим. изд.

Microsoft что в этом документе основное внимание уделяется применению для доступа к данным Microsoft SQL 2000 с использова нием SQL Server Data Provider Ч одного из двух провайдеров дан ных, поставляемых с ADO.NET. Там, где это нужно, в документе подчер киваются о следует знать при использовании OLE Data Provider для доступа к другим источникам данных с под держкой OLE DB.

Конкретную реализацию компонента доступа к данным, разработанного с применением принципов и в этом документе, см. в Data Access Application Block Обратите внимание, что для этой реализации исходный код, который можно напрямую использовать в ваших.NET-приложениях.

Кому адресован этот документ В этом документе описываются принципы, которыми должны руковод ствоваться архитекторы приложений и корпоративные разработчики при создании Его следует прочитать, если в ваши обязан ности входят проектирование и разработка уровня данных в многоуровне вом Что вы должны знать Чтобы это руководство для создания.NET-приложений, ходим практический опыт разработки кода для доступа к данным с ис пользованием ADO (ActiveX Data Objects) и/или OLE DB, а также опыт работы с SQL Server. Вы также должны знать, как разрабатывать управля емый код для платформы и быть в курсе фундаментальных измене ний, внесенных в модель доступа к данным с появлением ADO.NET. До полнительную по программированию для платформы см. по ссылке Введение в ADO.NET ADO.NET Ч модель доступа к данным. Ее можно ис пользовать для доступа к реляционным СУБД, таким как SQL Server 2000, и ко многим дополнительным источникам данных, для работы с которы ми провайдер OLE DB. В известной степени ADO.NET от ражает новейшие эволюционные достижения в развитии технологии ADO.

Однако в ADO.NET появился ряд серьезных изменений и выз ванных слабосвязанной природой Web-приложений и тем фактом, что по сути они отсоединены от баз данных. Сравнение ADO и ADO.NET см. в статье ADO.NET for the ADO Programmer в Руководство по архитектуре доступа к данным на 1. Одно из ключевых новшеств ADO.NET Ч замена ADO-объекта Recordset комбинацией объектов DataTable, DataSet, и DataReader.

DataTable представляет набор (collection) записей отдельной таблицы и в этом отношении аналогичен Recordset. DataSet представляет набор объек тов DataTable, а также содержит отношения и ограничения, используемые при связывании таблиц. На самом деле DataSet Ч это хранящаяся в памя ти реляционная структура данных со встроенной поддержкой XML (Ex tensible Markup Language).

Одна из основных особенностей объекта DataSet в том, что ему не извес тен источник данных, который использовался для его заполнения. Это отсоединенный, объект, который представляет некий набор данных и может передаваться от компонента к компоненту через различ ные уровни многоуровневого приложения. Кроме того, DataSet можно се в поток данных благодаря чему этот объект идеально подходит для передачи данных между гетерогенными платформами. Объ ект DataAdapter используется ADO.NET для двухстороннего обмена дан ными между DataSet и нижележащим источником данных. DataAdapter также предоставляет расширенные возможности в пакетном обновлении данных Ч функциональность, которая ранее поддерживалась Recordset.

На рис. 1 показана полная объектная модель DataSet.

DataSet Объект Table Column Constraints Constraint | Row Relation Рис. 1. Объектная модель DataSet Провайдеры данных В ADO.NET используются так называемые провайдеры данных (Data Providers) Они обеспечивают доступ к соответствующим источни 12 Microsoft кам данных и содержат четыре объекта (Connection, Command, DataReader и DataAdapter). В настоящее время с ADO.NET поставляют ся два провайдера:

Х SQL Server Data Provider. Предназначен для работы с базами данных Microsoft SQL Server 7.0 и более поздних версий. Оптимизиро ван для доступа к SQL Server и взаимодействует с ним напрямую по протоколу передачи данных SQL Server.

Всегда пользуйтесь этим провайдером при работе с SQL Server 7.0 или SQL Server 2000.

Х OLE Data Provider. Управляемый провайдер для источников данных OLE DB. Немного уступает по эффективности SQL Server Data Provider, так как с базой данных через уро вень OLE DB. Имейте в виду, что этим провайдером не поддерживает ся провайдер OLE DB для ODBC (Open Database Connectivity). Для источников данных ODBC используйте ODBC Data Provider, описанный ниже. Список провайдеров OLE DB, совместимых с см. по ссылке Остальные провайдеры данных в настоящее время находятся в состоянии бета-тестирования.

Х ODBC Data Provider. В данный момент доступна для загрузки первая бета-версия. Этот провайдер обеспечивает родной доступ к ODBC-драйверам так же, как и OLE DB Data Provider к род ным провайдерам OLE DB. Получить дополнительную информацию об ODBC и скачать можно по ссылке Х Управляемый провайдер для считывания XML из SQL Server 2000.

XML for SQL Server Web update 2 (в настоящий момент проходит бета тестирование) включает, помимо всего прочего, управляемый провай дер, предназначенный специально для считывания из SQL Server 2000. Дополнительную информацию об этом обновлении см. по ссыл ке Структура имен Типы (классы, структуры, перечислимые и связанные с каждым из провайдеров данных находятся в собственных пространствах имен.

Х Содержит типы SQL Server Data Provider, Руководство по архитектуре доступа к на платформе Х System.Data.OleDb. Содержит типы OLE DB Data Provider.

Х Содержит типы ODBC Data Provider.

Х Содержит типы, независимые от например DataSet Для каждого из провайдеров в его пространстве имен содержатся реали зации объектов Connection, Command, DataReader и DataAdapter. Имена реализаций объектов из пространства имен SqlClient начинаются с пре фикса а имена реализаций из пространства имен Ч с префик са OleDb. реализация объекта Connection из пространства имен SqlClient называется тогда как ее эквивалент из OleDb Ч OleDbConnection. Соответствующие реализации объекта Data Adapter называются SqlDataAdapter и OleDbDataAdapter.

Базовые принципы программирования Если вы собираетесь работать с различными источниками данных и пла нируете переносить свой код с одного источника данных на другой, поду майте о реализации интерфейсов и Adapter, принадлежащих пространству имен System.Data.

Все реализации объектов Connection, Command, DataReader и DataAdap ter должны поддерживать эти интерфейсы.

Дополнительную информацию о разработке провайдеров данных см.

по ссылке Рис. 2 иллюстрирует стек доступа ADO.NET к данным и взаимосвязь ADO.NET с другими технологиями доступа к данным, в частности с ADO и OLE DB. Кроме показаны два управляемых провайдера и основные объекты, входящие в модель ADO.NET.

Дополнительную информацию об эволюции ADO ADO.NET см. в статье Introducing ADO+: Data Access Services for the Microsoft Frame опубликованной в номере Magazine за ноябрь 2000 г.

Сравнение хранимых процедур и операторов SQL В подавляющем большинстве фрагментов кода, приводимых далее в этом документе, используются объекты для вызова хранимых про цедур, выполняющих операции над базами данных. В некоторых случаях вы не увидите объект SqlCommand, так как имя хранимой процедуры редается непосредственно объекту SqlDataAdapter. При этом на внутрен нем уровне все равно создается объект SqlCommand.

3 клиенты Управляемые клиенты AOO.NET E SQL TDS Oracle, SQL Server SQL Server Access версии 6. 7. и ниже и Рис. 2. Стек доступа к данным Используйте хранимые процедуры, а не операторы SQL по следующим причинам.

Х Применение хранимых обычно повышает производитель ность, так как база данных может оптимизировать план доступа к дан ным, используемым процедурой, и кэшировать эти данные для даль нейшего применения.

Х Хранимые процедуры можно индивидуально на уровне базы данных. Клиенту можно выдать разрешения на хранимой процедуры, не предоставляя разрешения на доступ к используемым при этом таблицам.

Х Хранимые процедуры проще в сопровождении, так как обычно легче изменить хранимую процедуру, чем жестко зашитый в развертывае мый компонент оператор SQL.

Руководство по архитектуре к данным на Х Хранимые процедуры создают дополнительный уровень абстракции от схемы базы данных. Клиенту хранимой процедуры не требуется что-либо знать ни о деталях работы хранимой процедуры, ни о схеме базы данных.

Х Хранимые процедуры уменьшают трафик, так как в этом слу чае операторы SQL выполняются в пакетном режиме, а не путем пере дачи многократных запросов от клиента.

Свойства и аргументы конструкторов Значения свойств объектов ADO.NET можно задавать напрямую или че рез аргументы конструктора. Так, следующие фрагменты кода функцио нально эквивалентны:

// Объект Command настраивается SqlCommand cmd = "SELECT - FROM PRODUCTS", conn );

// Предыдущая строка по функциональности эквивалентна // трем строкам, в которых свойства настраиваются явным образом cmd new = conn;

= "SELECT * PRODUCTS";

С точки зрения производительности, разница между этими двумя подхо дами незначительна, так как присвоение и получение значений свойств.NET-объектов выполняется гораздо эффективнее по сравнению с анало гичными операциями над Выбор в данном случае определяется личными предпочтениями и стилем кодирования. Однако явное присвоение значений свойствам облегчает понимание кода (особенно если вы не очень хорошо знакомы с объектной моделью ADO.NET) и упрощает отладку.

Примечание Раньше разработчикам на Microsoft Visual Basic избегать создания объектов операторами вида Dim x As New. В СОМ такой код мог привести к замыканию в процессе что вызы вало самые разнообразные ошибки. В такой проблемы нет.

Управление соединениями с базами данных Соединение с базой данных Ч критически важный, дорогостоящий и ог раниченный ресурс, особенно в многоуровневых Web-приложениях. Край не важно правильно управлять соединениями, так как от этого в значи тельной мере зависит общая масштабируемость приложения. Кроме того, тщательно продумайте, где хранить строки подключения. Вы должны выб рать настраиваемое и безопасное место их хранения.

Microsoft Управляя с базами данных и строками подключений, ста райтесь:

Х обеспечивать масштабируемость приложений путем за счет совместного использования пула соединений с базой данных несколькими клиентами;

Х придерживаться стратегии конфигурируемого и высокопроизводи тельного пула соединений;

Х использовать средства Windows при доступе к SQL Server;

Х избегать олицетворения (impersonation) на промежуточном уровне;

Х хранить строки подключений;

Х открывать соединения с базой данных как можно позже, а закрывать Ч как можно раньше.

В этом разделе рассказывается о создании пула соединений, что поможет вам выбрать наиболее подходящую стратегию для пула соединений из множества возможных вариантов. Здесь же рассматривается управление строками подключения к базам данных, их хранение и администрирова ние. Наконец, представлено два шаблона кода, обеспечивающего гаранти рованное закрытие соединения и его возврат в пул.

Создание пула соединений Создание пула соединений с базой данных приложению повтор но использовать соединения из пула вместо многократного установления новых. Этот прием может значительно повысить масштаби руемость приложения, так как ограниченное число соединений позволяет обслуживать намного большее количество клиентов. Кроме того, поскольку вы избегаете значительных затрат времени на новых соединений.

Такие технологии доступа к данным, как ODBC и OLE DB, поддержива ют свои разновидности пулов соединений, в той или иной мере допуска ющих конфигурирование. Оба подхода практически прозрачны для клиен тского приложения, работающего с базой данных. Пул соединений OLE DB часто называют сеансовым (session pooling) или ресурсным (resource pooling).

Общее описание создания пулов соединений в (Microsoft Data Access Components) см. по ссылке Провайдеры данных обеспечивают незаметное для пользовате ля создание пула точный механизм которого зависит от про по архитектуре доступа к данным на платформе вайдера. В этом разделе рассматривается создание пула соединений для следующих провайдеров:

Х Server Data Provider Х OLE Data Provider ( Поддержка пула соединений в SQL Server Data Provider Если Вы работаете с SQL Server Data Provider, используйте поддер жку пулов соединений, предлагаемую провайдером. Это эффективный механизм с поддержкой транзакций, реализуемый в управляемом коде са мого провайдера. Пулы создаются для каждого процесса отдельно и не уничтожаются до завершения соответствующего процесса.

Этот вид пула соединений можно использовать прозрачно, но при этом следует знать, как управлять пулами и как задействовать различные воз можности конфигурирования для более точной настройки пулов, Настройка пула соединений в SQL Server Data Provider Пул настраивается с помощью набора пар указываемых в строке подключения. Например, можно настроить, разрешено ли созда ние пула (по умолчанию Ч разрешено), указать максимальный и мини мальный размер пула, а также время ожидания на открытие соеди нения, поставленного в очередь. В приведенном ниже примере показана строка подключения, в которой заданы максимальный и минимальный размеры пула.

Integrated Pool Slze=75;

Pool Когда соединение открыто и пул создан, в пул добавляется столько соеди нений, сколько нужно, чтобы довести их число до заданного минимума.

Впоследствии соединения могут добавляться в пул до тех пор, пока их число не достигнет максимума, установленного при настройке. После это го новые запросы на открытие соединений ставятся в очередь, причем вре мя ожидания также настраивается.

Выбор размера пула Возможность устанавливать максимальный предел очень важна для круп номасштабных систем, управляющих параллельными запросами многих тысяч клиентов. Чтобы выяснить оптимальные размеры пула для вашей Microsoft ADO.NET системы, необходимо понаблюдать за ним и за производительностью при ложения. Оптимальный размер также зависит от аппаратных средств, на которых работает SQL Server.

В процессе разработки вы, вероятно, захотите уменьшить заданный по умолчанию максимальный пула чтобы упростить поиск уте чек соединений.

Если минимальный размер пула задан, возможно небольшое падение про изводительности после первоначального заполнения пула до этого уров ня, хотя первые несколько подключившихся клиентов получат выигрыш.

Заметьте, что новые соединения создаются последовательно, т. е. сервер не будет одновременными запросами при первоначальном запол нении пула.

Дополнительную информацию о наблюдении за пулами соединений см. в разделе этого документа за пулами соединений.

Полный список ключевых слов, используемых в строках подключений при создании пула соединения, см. по ссылке Дополнительная информация При использовании пула соединений в SQL Server Data Provider имейте в виду следующее.

Х Соединения в пул алгоритму строгого соответствия (exact match algorithm) строк подключения. Механизм поддержки пула чувствителен даже к пробелам между парами Так, следующие две строки подключения приведут к созданию двух разных пулов из-за того, что вторая строка содержит дополнительные пробелы.

conn = new // создается пул А conn = new SqlConnection( "Integrated ;

// пул В содержит пробелы) Х В бета-версиях Framework пул соединений всегда отключается при выполнении приложения под отладчиком. Без отладчика пул со здается и в отладочном (debug), и в финальном (release) вариантах приложения. В RTM-версии (Released To Manufacture) Frame work это ограничение снято, и пул создается во всех случаях.

Руководство по архитектуре доступа к на платформе Х Пул соединений делится на несколько специфичных для конкретных транзакций пулов и еще выделяется один пул для соединений, не используемых транзакциями в данный момент. Для потоков, сопостав ленных с контекстом определенной транзакции, соеди нение из того пула, который включает соединения, участвующие в дан ной транзакции. Благодаря этому работа с соединениями, используе мыми транзакциями, становится транспарентной.

Поддержка пула соединений в OLE DB Data Provider OLE DB Data Provider поддерживает пулы соединений, обращаясь к соответствующим сервисам механизма поддержки ресурсных пулов в OLE DB. Настройка ресурсного пула возможна несколькими способами:

Х использованием строк подключения для настройки, включения и от ключения поддержки ресурсного пула соединений;

Х через реестр;

Х программным конфигурированием ресурсного пула.

Во избежание проблем при развертывании, возникающих в случае на стройки пулов через реестр, не пользуйтесь этим способом настройки ре сурсного пула OLE DB.

Подробнее о создании ресурсного пула соединений OLE DB см. в руководство OLE DB Programmer's (глава 19 OLE DB Ser vices, раздел Управление пулами соединений с помощью объектов пула Как разработчик для Windows DNA, вы можете отключить создание ресур сного пула OLE DB и/или создание пула соединений ODBC и использо вать в качестве пула соединений к базе данных объектный пул На то могут быть две основных причины:

Х размеры пулов и пороговые значения можно настроить явным образом (в Catalog);

Х производительность пула объектов может быть в 2 раза больше произ водительности стандартного пула.

Однако, так как SQL Server Data Provider работает с пулом соедине ний на внутреннем уровне, при использовании этого провайдера нет необ ходимости разрабатывать собственный объектный механизм поддержки пула. Таким вы избежите сложностей, связанных с включением ресурсов в транзакции вручную (manual transaction enlistment).

20 Microsoft Если вы используете OLE DB Data Provider, а также хотите добить ся удобства настройки и повышения производительности, стоит о поддержке объектного пула Если в этих целях вы те объект пула (pooled object), отключите в OLE DB ресурсный пул и ав томатическое включение ресурсов в транзакции (например, указав в строке подключения OLE DB В своей реализации объекта пула вам придется самостоятельно управлять включением ресурсов в транзакции.

Наблюдение за пулами соединений Для наблюдения за тем, как приложение работает с пулом соединений, можно воспользоваться утилитой Profiler, поставляемой с SQL Server, или оснасткой Performance Monitor в Microsoft Windows 2000.

Для наблюдения за пулом с помощью Profiler выполните следующие дей ствия.

1. Откройте Start Programs Microsoft SQL Server Profiler для запус ка Profiler.

2. Выберите File | New Trace.

3. Укажите параметры соединения и щелкните ОК.

4. В диалоговом окне Trace Properties перейдите на вкладку Events.

5. В списке Selected event>

6. Щелкните Run, чтобы начать трассировку. Вы увидите события Audit Login при установлении а события Audit Logout Ч при закрытии соединений.

Для наблюдения за пулом с помощью Performance Monitor выполните сле дующие действия.

1. Откройте Start Programs Administrative Tools | Performance для за пуска Performance Monitor.

2. Щелкните правой кнопкой мыши график, показываемый в окне, и вы берите Add Counters.

3. В списке Performance object укажите SQL Server: General Statistics.

4. В появившемся списке щелкните User Connections.

5. Щелкните Add, затем Ч Close.

Примечание Framework предоставляет дополнительный набор счетчиков производительности, позволяющих с помощью Performance Monitor на Руководство по доступа к иа платформе блюдать и накапливать статистику использования пула соединений для SQL Server Data Provider.

Управление защитой Хотя пул соединений повышает общую масштабируемость приложения, он лишает вас возможности управлять защитой базы данных. Это объяс няется тем, что для поддержки пула нужно, чтобы все строки ния были одинаковыми. Если вы хотите отслеживать операции над базой данных, выполняемые каждым из пользователей, подумайте о добавлении для каждой операции параметра, через который передается идентифика ция пользователя (имя и пароль);

тем самым сможете вручную регист рировать действия, выполняемые пользователями в базе данных.

Аутентификация средствами Windows При подключении к SQL Server используйте аутентификацию средствами Windows, так как это дает ряд преимуществ.

Х Упрощается управление защитой, поскольку вы имеете дело с одной моделью защиты (предоставляемой Windows), не используя еще и мо дель защиты SQL Server.

Х Имена и пароли пользователей не включаются в строки подключения.

Х Имена и пароли пользователей не передаются по сети открытым текстом.

Х Повышается безопасность входа в базу данных за счет поддержки сро ков действия паролей, минимальной длины пароля и блокировки учет ной записи после неоднократных неудачных попыток информация При аутентификации доступа к SQL Server средствами Windows учиты вайте соображения, изложенные в следующих разделах.

Производительность Тестирование производительности Beta 2 что при аутенти фикации средствами Windows на открытие соединения из пула уходит больше времени, чем при аутентификации средствами SQL Server. Одна ко, хотя аутентификация Windows работает медленнее, снижение произ водительности незначительно по сравнению со временем, требуемым для выполнения запроса или хранимой процедуры. В итоге преимущества аутентификации Windows обычно перевешивают небольшое уменьшение производительности.

Также следует ожидать, что различия в скорости аутентификации сред ствами Windows и средствами SQL Server при открытии входящих в пул соединений станут менее заметными в Framework.

22 ADO.NET Избегайте олицетворения на промежуточном уровне Аутентификация через требует для доступа к базе данных учет ную запись Windows. Хотя применение олицетворения на промежуточном уровне (при подключении к SQL Server) может показаться логичным, вы должны этого, так как тогда теряет смысл поддержка пула соеди нений и резко снижается масштабируемость приложения.

Чтобы решить эту проблему, вместо обычных учетных записей, под кото рыми пользователи входят в Windows, используйте для подключения к SQL Server набор учетных записей Windows, каждая из которых соответствует определенной роли.

Попробуйте, например, такой подход.

Х Создайте две учетные записи Windows: одну Ч для операций чтения, другую Ч для операций записи. (Или несколько учетных записей, ко торые отражают роли, определяемые логикой приложения. Например, вы могли бы задействовать одну учетную запись для пользователей Интернета, а другую Ч для пользователей, работающих только в ло кальной сети, и/или администраторов.) Х Сопоставьте каждую учетную запись с ролью, определенной в базе данных SQL Server, и установите для каждой роли необходимые пра ва для доступа к базе данных.

Х Перед выполнением какой-либо операции над базой данных опреде ляйте через прикладную логику своего уровня доступа к данным, ка кую учетную запись Windows следует использовать для доступа к SQL Server.

Примечание Каждая учетная запись должна находиться в том же домене, что и IIS (Internet Information Services) и SQL Server, или в доверяемых доменах;

можно также создать соответствующие учетные записи (с одними и теми же именем и паролем) на каждом компьютере.

Используйте в качестве сетевой библиотеки TCP/IP SQL Server 7.0 и более поздних версий поддерживает аутентификацию средствами Windows для всех сетевых библиотек. Используйте TCP/IP, чтобы добиться выигрыша в возможностях конфигурирования, произво дительности и масштабируемости. Более подробную информацию о при менении TCP/IP см. в разделе Подключение через брандмауэры далее в этом документе.

Хранение строк подключения Строки подключения можно хранить несколькими способами, различаю щимися по уровням гибкости и Хотя наилучшую производитель по доступа к на платформе ность строк подключения в исходный код, меха низм кэширования файловой системы гарантирует, что снижение произво дительности, связанное с хранением строк подключения вне будет пренебрежимо мало. Кроме дополнительная гибкость при хра нении строк подключения вне кода приложения, позволяющая админист ратору изменять конфигурацию доступа к данным, практически во всех случаях предпочтительнее небольшого выигрыша в производительности.

Выбор способа хранения строк подключения определяется двумя важней шими факторами: безопасностью и простотой настройки;

следует принять во внимание и чуть менее важный фактор Ч производительность.

Строки подключения к базе данных можно хранить в:

Х файле конфигурации приложения например в файле Web.config Web-приложения ASP.NET;

Х UDL-файле (Universal Data Link) ( Ч этот способ годится только для OLE DB Data Provider;

Х реестре Windows Х собственном файле ( Х COM+ используя строки инициали зации (только для обслуживаемых компонентов), При доступе к SQL Server средствами аутентификации Windows можно избежать хранения имен пользователей и паролей в строках подключения.

Если вам нужен более высокий уровень защиты, храните строки подклю чения в зашифрованном виде.

Безопасное и конфигурируемое решение для Web-приложений ASP.NET хранение строк подключения в зашифрованном виде в файле Web.config.

Примечание В строке подключения именованному значению Persist Security Info можно присвоить false, чтобы параметры, связанные с безопасностью, например пароль, не возвращались свойством ConnectionString объекта SqIConnection или OleDbConnection.

В следующих разделах рассматривается, как использовать различные ва рианты хранения строк подключения, и показываются преимущества и недостатки каждого подхода. Это позволит вам сделать обоснованный выбор, отвечающий условиям работы вашего приложения.

Применение конфигурационных XML-файлов приложения Для хранения строк подключения к базе данных можно элементом в разделе нестандартных параметров конфигура ционного файла приложения. В этом элементе могут присутствовать про извольные пары как показано в приведенном ниже фраг менте кода.

Odd Элемент следует за элементом , а не непосредственно за Преимущества Х Простота в распространении. Строка подключения передается вмес те с конфигурационными файлами с помощью обычного средства раз вертывания хсору, применяемого в Х Простота в программном доступе. Благодаря свойству AppSettings класса считать строку подключения к базе дан ных в период выполнения приложения очень легко.

Х Поддержка динамического обновления (только в ASP.NET). Если администратор обновляет строку подключения в файле данное изменение вступает в силу при следующем обращении к стро ке подключения Ч для компонента, не поддерживающего состояния (stateless component), это произойдет скорее всего при следующем обращении клиента к компоненту для выполнения запроса к базе данных.

Недостатки Х Проблема с защитой. Хотя DLL интерфейса (Internet Server Application Programming Interface) ASP.NET не допускает прямого об ращения к файлам с расширением.config, а для еще больше го ограничения доступа можно использовать разрешения файловой системы NTFS, вас все равно может не устроить хранение параметров подключения в виде незашифрованного текста на Web-сервере, взаи модействующем с клиентами. Для большей безопасности храните стро ки подключения в конфигурационном файле в зашифрованном виде.

Руководство по архитектуре доступа к данным на платформе Дополнительная Х Нестандартные (custom), или пользовательские, параметры приложе ния можно считывать через статическое свойство класса Это демонстрирует приве денный ниже фрагмент кода, где что вы считываете показанный ранее нестандартный ключ DBConnStr:

using private string { return Х Дополнительную информацию о конфигурировании приложений Framework см. по ссылке Применение OLE DB Data Provider позволяет указывать в строках подключений имена UDL-файлов (Universal Data Link). Вы можете передавать строку подключения как аргумент конструктора объекта или присваивать ее свойству ConnectionString этого объекта, Примечание SQL Server Data Provider не поддерживает в своих строках подключения. Таким образом, этот способ годится только при работе с OLE DB Data Provider.

При работе с провайдером OLE DB для ссылки на используй те в строке подключения конструкцию Name=name.udl>>.

Преимущества Х Стандартный подход. Возможно, вы уже используете UDL-файлы для управления строками подключения.

Недостатки Х Меньшая производительность. Строки подключения со ссылками на UDL-файлы читаются и анализируются при каждом открытии соеди нения.

Х Проблема защиты. UDL-файлы хранятся как простой текст. Вы може те защищать их с помощью разрешений но тогда возникают те же проблемы, что и с файлами.config.

Х SqlClient не поддерживает UDL-файлы. Этот подход не поддержива ется SQL Server Data Provider, для доступа к SQL Server версии 7.0 и выше.

Дополнительная информация Х Вы должны позаботиться, чтобы у администраторов был доступ к фай лу для чтения/записи, а у учетной записи, под которой запускается приложение, Ч доступ только для чтения. Рабочий процесс Web-при ложения по умолчанию выполняется под учетной записью SYSTEM, но ее можно переопределить через элемент process > конфигурационного файла (Machine.config). Кроме того, данную учетную запись можно подменить другой (тоже зарегистриро ванной в системе) через элемент файла Х Работая с Web-приложениями, убедитесь, что не находит ся в виртуальном каталоге, Ч в ином случае появится потенциальная возможность скачивания этого файла через Web.

Х Дополнительную информацию об этих и других особенностях ASP.NET, связанных с безопасностью, см. по ссылке Применение реестра Windows Для хранения строки подключения можно создать свой раздел в реестре Windows, но это не рекомендуется, так как тогда возникают проблемы с развертыванием приложения.

Преимущества Х Безопасность. Доступ к определенным разделам реестра можно конт ролировать через списки управления доступом (access control lists, ACL). Для большей безопасности используйте шифрование.

Х Простота программного доступа. В есть классы, позволяющие читать из реестра.

Недостатки Х Проблемы с развертыванием. Соответствующие параметры реестра придется распространять вместе с приложением, что не позволит в полной мере использовать преимущества хсору.

Применение файла собственного формата Строку подключения можно хранить в файле собственного формата. Но этот способ не дает никаких преимуществ и поэтому не рекомендуется, Преимущества Х Нет.

Руководство по архитектуре доступа к на платформе Недостатки Х Дополнительные усилия программировании. Такой подход требует дополнительных усилий в программировании и создает сложности в поддержке одновременного доступа.

Х Проблема развертывания. Файл придется копировать вместе с ос тальными файлами приложения Не помещайте файл в ката лог приложения ASP.NET или его подкаталог, чтобы этот файл нельзя было скачать через Web.

Использование Catalog Строку подключения к базе данных можно хранить в Catalog Ч тогда она будет автоматически передаваться вашему объекту через строку инициализации объекта (object construction string). COM+ будет вызы вать метод Construct объекта сразу после создания экземпляра объекта, передавая указанную строку инициализации.

Примечание Этот способ годится только для обслуживаемых компонентов (ser viced components). Его стоит рассматривать, только если ваши управляемые ком поненты используют другие сервисы, например поддержку распределенных тран закций или объектных пулов.

Преимущества Х Администрирование. Администратор может легко настраивать строку подключения через оснастку Component Services консоли ММС.

Недостатки Х Проблемы с безопасностью. Catalog считается небезопасным хранилищем данных (хотя доступ к нему можно ограничить с помо щью ролей СОМ+), поэтому помещать в него строки в виде незашиф рованного текста нельзя.

Х Проблемы с развертыванием. Элементы СОМ+ Catalog должны рас пространяться вместе с.NET-приложением. Если вы используете дру гие сервисы масштаба предприятия, например распределенные тран закции или поддержку объектных пулов, то хранение строки подклю чения к базе данных в этом каталоге не приводит к дополнительным издержкам при развертывании, поскольку тогда СОМ+ Catalog все равно нужно развертывать для поддержки этих сервисов.

Х Компоненты должны быть обслуживаемыми. Строки инициализации можно использовать только для обслуживаемых компонентов. Не на следуйте класс своего компонента от (что делает компонент обслуживаемым) лишь для того, чтобы получить возмож ность работать со строками инициализации.

28 Microsoft Дополнительная информация Х Подробнее о том, как настраивать на конструирование объ екта (object construction), см. в приложении Как включить поддержку конструирования объектов ( library Х Подробнее о разработке обслуживаемых компонентов см. по ссылке Примеры использования соединений Независимо от провайдера данных вы должны всегда соблюдать сле правила.

Х Открывать соединение с базой данных как можно позже.

Х Использовать это соединение в течение как можно более короткого пе риода.

Х Закрывать как можно быстрее. Соединение не возвращается в пул, пока оно не закрыто вызовом метода Close или Dispose. Его также сле дует закрывать, если вы что оно разорвано. В последнем случае соединение возврашается в пул и помечается как неработоспо собное. Компонент, управляющий пулом объектов (object pooler), пе риодически сканирует пул и ищет объекты, помеченные как неработос пособные.

Чтобы гарантированно закрывать соединение до того, как метод возвраща ет управление, используйте один из подходов, которые проиллюстрирова ны двумя примерами кода (см. ниже). В первом применяется блок finally, а во втором Ч оператор using языка С#, обеспечивающий вызов метода Dispose объекта.

В первом фрагменте кода соединение будет обязательно закрыто в блоке finally. Заметьте, что этот способ работает не только в С#, но и в Visual Basic так как последний тоже поддерживает структурную обработ ку исключений (structured exception handling, SEH).

public void { = new new conn );

= Руководство по архитектуре доступа на catch (Exception e) // Обрабатываем и протоколируем ошибку finally } Второй фрагмент кода иллюстрирует альтернативный подход с примене нием оператора using языка С#. Обратите что в Visual Basic нет оператора using или его эквивалента.

public void // using что для объекта conn будет вызван // Dispose, и соединение будет закрыто.

using (SqlConnection conn = new cmd = new SqlCommandC'CommandProc", conn);

= Этот подход применим и к другим объектам, например к SqlDataReader или которые нужно закрывать перед тем, как в рамках текущего соединения делать что-то еще.

Обработка ошибок Ошибки ADO.NET генерируются и обрабатываются через нижележащую поддержку SEH Ч неотъемлемую часть Framework. Благодаря му ошибки при кода доступа к данным обрабатываются но так же, как и ошибки, возникающие в любом другом месте приложения.

Исключения обнаруживаются и обрабатываются по стандартному для синтаксису и стандартными приемами.

В этом разделе как разрабатывать надежный код доступа к данным, и объясняется, как обрабатывать соответствующие ошибки. Здесь же вы найдете руководство по обработке исключений, специфичных для SQL Server Data Provider.

Провайдеры данных транслируют ошибки, специфичные для баз данных, в стандартные которые вы должны вать в коде доступа к данным. Параметры ошибки, относящиеся к базе данных, доступны через свойства соответствующего объекта исключения.

Все типы в конечном счете наследуются от базового класса Exception в пространстве имен System. Провайдеры данных генерируют типы исключений, специфичные для конкретного провайдера.

Например, SQL Server Data Provider генерирует объекты SqlExcep tion, когда в SQL Server возникает ошибочная ситуация. Аналогично OLE DB Data Provider генерирует исключения типа OleDbException с параметрами, предоставленными нижележащим провайдером На рис. 3 показана иерархия исключений, генерируемых провайдерами данных Заметьте, что класс OleDbException наследуется от External Exception Ч базового класса для всех исключений COM Свойство ErrorCode этого объекта содержит COM сгенерированный OLE DB.

Exception Exception System cm System Exception External OleDbException SQL Server Data Provider Data Prov Рис. З. Иерархия исключений провайдеров данных Перехват и обработка Для обработки исключений при доступе к данным поместите свой код доступа к данным в блок try и перехватывайте генерируемые исключения в блоках catch с соответствующими фильтрами. Так, в коде доступа к дан ным, работающем с SQL Server Data Provider, следует перехватывать исключения типа SqlException, как показано в коде, приведенном ниже.

try { // Код доступа к данным \ catch (SqlException // самое специфичное исключение > catch (Exception ex) // самое универсальное // специфичное) исключение '.

по архитектуре доступа к данным на платформе Если у вас несколько блоков catch с разными критериями фильтрации, не забывайте располагать их в порядке от наиболее специфичных к наименее специфичным (наиболее универсальным). Это гарантирует выполнение самого специфичного блока catch.

Ряд свойств класса SqlException предоставляет дополнительную мацию об исключительной ситуации;

Х Message Ч текст, описывающий ошибку;

Х Number Ч номер уникально идентифицирующий ее тип;

Х State Ч дополнительная информация о причине возникновения ки. Например, если в хранимой процедуре есть несколько строк, спо собных привести к одной и той же ошибке, свойство State позволяет определить конкретное место, где она возникла;

Х Errors Ч набор (collection) с подробными сведениями об ошибках, ге нерируемых SQL Server. В этот набор всегда входит минимум один объект типа SqlError.

В следующем фрагменте кода показывается, как обрабатывать ошибочные ситуации, возникающие в SQL Server при работе с SQL Server Data Provider.

using using // Метод, предоставляемый DAL (Data Access public string int ) { SqlConnection conn = new SqlConnection( // Весь код доступа к данным помещаем в блок try I = conn );

= ProductID = = // Блок finally выполняется до того, как метод возвращает управление return 32 Microsoft catch { // исключение, возникшее при доступе к данным, и протоколируем сведения о нем // Включаем текущее исключение в более внешнее // и повторно генерируем throw new DALException( "Unknown + sqlex );

} catch (Exception ex) { // Обработка универсальных throw ex;

} finally // соединение будет закрыто в любом случае \ // процедура, заносящая сведения из SqlException // в журнал событий приложения private void SqlException sqlex ) EventLog el = new = string strMessage;

strMessage = "Exception : + + has occurred";

strMessage );

(SqlError in { strMessage = + + Number: + Procedure: + + Server: + + Source: + + State: + + Severity: + + + );

В блоке catch для SqlException сначала регистрируются параметры исклю чения с помощью вспомогательной функции LogException. В ней операто ром foreach перебираются специфичные для провайдера объекты из набо ра Errors, и полученная информация записывается в журнал ошибок. За по архитектуре доступа к данным иа платформе тем исключение SQL Server в исключение типа DALException, имеющее больший смысл для методов, вызывающих Для передачи этого исключения вызывающему методу используется ключевое слово throw.

Дополнительная информация Х Полный список членов класса см. по ссылке asp.

Х Дополнительные сведения о разработке собственных исключений, протоколировании.NET-исключений и включении их в оболочки, а также о различных подходах к передаче исключений см. по ссылке Генерация ошибок в хранимых процедурах В имеется функция RAISERROR (обратите внима ние на регистр букв), генерировать нестандартные ошибки и возвращать их клиенту. В случае клиентов ADO.NET провайдер SQL Server Data Provider перехватывает эти ошибки и преобразует их в объекты SqlError.

Самый простой способ вызова функции RAISERROR Ч передать в каче стве первого параметра текст сообщения, а затем задать параметры, опре уровень значимости ошибки (severity) и Product ID: Xs', 1, ) Здесь параметр подстановки используется, чтобы вернуть в сообщении об ошибке текущий идентификатор некоего продукта. Второй параметр уровень значимости, а третий Ч состояние сообщения.

Дополнительная информация Х Чтобы избежать текста сообщений в код, добавьте свои со общения в таблицу вызвав системную хранимую процеду ру sp_addmessage или воспользовавшись Enterprise Manager в SQL Server. Тогда вы сможете ссылаться на нужное сообщение, его идентификатор функции RAISERROR. Идентификаторы ваших сообщений должны быть больше 50 000;

50001, 16, 1, ) Х Полную информацию о функции RAISERROR ищите по предметному указателю в SQL Server Books Online.

Microsoft Правильное использование уровней значимости Тщательно выбирайте уровни значимости (severity levels) для определяе мых вами ошибок и учитывайте влияние каждого на работу при ложения. Уровни значимости ошибок варьируются в диапазоне от 0 до и указывают тип с которой столкнулся SQL Server 2000. В кли ентском коде вы можете выяснить уровень значимости ошибки через свой ство>

Табл. 1. Уровни значимости ошибок: влияние и смысл Уровень Закрывается ли Генерируется ли значимости соединение Описание 10 и Нет Нет Информационные не обязательно с ошибочными ситуациями 11-16 Нет Да Ошибки, которые могут быть устранены например кой выполнения операции с исправленными входными данными 17-19 Нет Да Ошибки или системы 20-25 Да Да Фатальные системные ошибки (в том числе аппаратные);

соединение с клиентом Управление автоматическими При любых ошибках с уровнем выше 10 провайдер SQL Server Data Provider генерирует SqlException. Если компонент участвует в автоматической транзакции (транзакции и обнаруживает SqlEx ception, он должен за отмену транзакции. Это может быть сделано автоматически или вручную Ч в зависимости от того, помечен ли метод атрибутом Подробнее об обработке SqlException в контексте автоматических транзак ций см. в разделе Определение результатов этого документа.

Получение информационных сообщений Уровни значимости от 10 и ниже используются для передачи информаци онных сообщений и не вызывают генерации SqlException.

Руководство по архитектуре к данным на платформе Чтобы получать информационные сообщения, действуйте по следующей схеме.

Х Создайте обработчик события и подключите его к событию sage объекта SqlConnection. Делегат этого события показан в следую щем фрагменте кода.

public delegate void object sender, SqllnfoMessageEventArgs e );

Данные сообщения доступны через объект пере даваемый вашему обработчику события. У этого объекта имеется свойство Errors, содержащее несколько объектов SqlError Ч по одному на каждое информационное сообщение. Ниже показано, как зарегистрировать обра ботчик события, протоколирующий информационные сообщения.

public string int ) ( SqlConnection conn = new i // Регистрируем обработчик события для информационного сообщения += new SqlInfoMessageEventHandler( };

// объект команды и выполняем его catch (SqlException { // Протоколируем и исключение ;

Х finally // Обработчик события для информационного сообщения void object sender, e ) { foreach( SqlError in ) // Протоколируем содержимое свойств SqlError Производительность В этом разделе рассматривается несколько типичных сценариев доступа к данным, и для каждого из них дается подробное описание наиболее масш табируемого и производительного решения, реализуемого в виде ко торый использует ADO.NET. Там, где это имеет смысл, сравниваются про изводительность, функциональность и трудоемкость разработки. Вот эти сценарии.

Х Чтение нескольких записей. Считывается набор результатов (result set), затем полученные записи перебираются в цикле.

Х Чтение одной записи. Считывается одна запись с заданным первич ным ключом.

Х Чтение одного поля. Считывается одно поле (item) из заданной записи.

Х Проверка существования элемента данных. Проверяется, есть ли за пись с заданным первичным ключом. Это не более чем разновидность сценария чтения одного поля, при которой достаточно вернуть простое булево значение.

Чтение нескольких записей В данном случае нужно считать табличный набор данных и перебрать по лученные записи, чтобы выполнить определенную операцию. Например, вам может понадобиться считать набор данных, обработать его в отсоеди ненном виде и передать клиентскому приложению в виде XML-докумен та (возможно, посредством Web-сервиса). Или такая ситуация: данные нужно показать в виде HTML-таблицы.

Чтобы определиться с выбором наиболее подходящего способа доступа к выясните, что вам важнее: повышенная гибкость отсоединенного объекта DataSet или голая производительность объекта SqlDataReader, идеального для отображения данных в электронной коммерции между предприятием и потребителем В2С). Эти два основных подхода показаны на рис. 4.

Примечание SqIDataAdapter, применяемый для заполнения DataSet, обращает ся к данным на внутреннем уровне через SqlDataReader.

Сравнение доступных вариантов Выборка нескольких записей из источника данных возможна следующи ми способами.

Х Использовать объект SqIDataAdapter для генерации DataSet или DataTable.

по доступа к данным на платформе (Web-сервисы / с данными с набора данных е и ное х Рис. 4. Типичные сценарии считывания нескольких записей Х Задействовать SqlDataReader для создания потока данных только для чтения только вперед.

Х Применить для создания потока XML-данных только для чтения в направлении только вперед.

Выбор между SqlDataReader и Ч это фактически выбор между производительностью и функциональностью. SqlDataReader обес печивает оптимальную производительность, a DataSet Ч дополнительную гибкость и функциональность.

Связывание с данными Все эти три объекта могут выступать в качестве источников данных для элементов управления, связываемых с данными (data-bound controls), но DataSet и DataTable способны работать с более широкой группой элемен тов чем SqlDataReader. Это объясняется тем, что DataSet и DataTable реализуют интерфейс IListSource (возвращающий тогда как SqlDataReader реализует интерфейс Некоторые элемен ты управления Windows Forms поддерживают связывание с данными, если их источники реализуют IList, Различие между объектами объясняется их разным предназначением, DataSet (включающий в себя DataTable) Ч это мощная отсоединенная подходящая для работы как с Web-формами, так и с Windows 38 Microsoft Forms. С другой стороны, класс чтения данных (data reader) оптимизиро ван для Web-приложений, требующих быстрого к данным на только вперед.

Так что проверяйте требования к источникам данных тех элементов управ ления, которые вы собираетесь связывать с данными.

Передача данных между уровнями приложения DataSet дает реляционное представление данных, с которым можно рабо тать и как с а также позволяет передавать отсоединенную копию данных между уровнями и компонентами приложения.

Однако SqlDataReader обеспечивает оптимальную производительность из-за того, что не тратит память и время на создание DataSet. Запомните, что создание объекта DataSet часто влечет за собой создание массы по добъектов вроде DataTable, и и что объекты-наборы служат контейнерами для этих Применение DataSet Используйте DataSet, заполняемый объектом SqlDataAdapter, если:

Х вам нужен размещенный в памяти отсоединенный кэш данных, кото рый можно было бы передавать другим компонентам или уровням приложения;

Х вам требуется реляционное данных в обрабаты ваемых либо в формате XML, либо в другом формате;

Х вы работаете с данными, получаемыми из нескольких источников дан ных, например из нескольких баз данных, таблиц или файлов;

Х вы хотите изменить все или отдельные полученные записи, используя механизм пакетного обновления, встроенный в SqlDataAdapter;

Х вы хотите связать с данными элемент управления, который поддержи вает источник, реализующий информация Используя SqlDataAdapter для генерации DataSet или DataTable. имейте в виду следующее.

Х Явно открывать или закрывать соединение с базой данных не требует ся. Метод Fill объекта SqlDataAdapter открывает соединение с базой данных, а перед возвратом управления закрывает его. Если соединение уже открыто, Fill оставляет его открытым.

Х Если соединение нужно для каких-то других целей, откройте его перед вызовом метода Fill. Тогда вы избежите лишних открытий/закрытий соединения и выигрыш в производительности.

по архитектуре доступа к данным платформе -NET Х Хотя один и тот же объект SqlCommand можно использовать повтор но, чтобы несколько раз выполнить одну и ту же команду (оператор SQL или хранимую процедуру), не делайте этого для выполнения раз ных команд.

Х Пример кода, показывающий, как с помощью Adapter запол нить DataSet или DataTable, см. в приложении: Использование DataAdapter для чтения нескольких записей.

Применение SqIDataReader Используйте SqIDataReader, получаемый вызове метода der объекта SqlCommand, если:

Х вы имеете дело с большими объемами данных Ч слишком большими, чтобы они могли уместиться в одном кэше;

вам нужно уменьшить объем памяти, занимаемый приложением;

Х вы хотите избежать издержек, связанных с созданием объектов при ис пользовании DataSet;

вы хотите связать с данными элемент управления, который поддержи вает источник, реализующий интерфейс вам нужно упростить и оптимизировать доступ к данным;

вы считываете записи, содержащие поля с большими двоичными объ ектами (binary large objects, BLOB). SqIDataReader позволяет считы вать с разбиением на удобные для обработки порции.

Подробнее об обработке BLOB см. в разделе этого документа Работа с большими двоичными объектами Дополнительная информация При использовании SqIDataReader имейте в виду следующее.

Соединение с базой данных остается открытым и не может использо ваться в других целях, пока активен класс чтения данных. Вызывайте метод Close объекта SqIDataReader как можно быстрее.

Х На одно соединение приходится только один класс чтения.

Х Вы можете явно соединение, закончив работу с классом чте ния, или увязать срок существования соединения со сроком жизни объекта SqIDataReader, передав методу значение Сот mandBehavior.CloseConnection. Этот параметр что соедине ние должно быть закрыто, как только будет закрыт SqIDataReader, Х При доступе к данным с помощью класса чтения используйте типизи рованные (typed accessor methods) (например, Get Int32 или GetString), если вам известен тип данных поля, Ч тогда со кращается число преобразований типов, выполняемых при чтении данных поля.

Х Чтобы избежать лишней передачи данных от сервера клиенту в том случае, когда вы хотите закрыть класс чтения и отбросить оставшиеся результаты, вызовите метод Cancel объекта команды перед вызовом метода Close класса чтения. Вызов Cancel гарантирует, что результаты будут отброшены на сервере и не попадут к клиенту. Вызов только метода Close класса чтения данных, напротив, приведет к тому, что оставшиеся результаты будут без всякой необходимости переданы с сервера, чтобы опустошить поток данных.

Х Если вам нужно получить выходные параметры хранимой процедуры или ее возвращаемое значение и вы используете метод ExecuteReader объекта SqlCommand, обязательно вызовите метод Close класса чте ния перед обращением к выходным параметра или возвращаемому значению.

Х Пример кода, показывающий, как работать с SqlDataReader, см. в прило жении Использование SqlDataReader для чтения нескольких Применение XmlReader, получаемый при вызове метода Reader объекта SqlCommand, в следующих случаях.

Х Считываемые данные обрабатываются как но нужно избежать издержек, связанных с созданием DataSet, и нет необходимости в от соединенном кэше данных.

Х Требуется блока FOR XML оператора SQL, позво ляющая гибко считывать из базы данных XML-фрагменты (т. е. XML документы без корневого элемента). Такой подход дает возможность, например, указывать точные имена элементов независимо от того, надо ли использовать схему, основанную на элементах или атрибутах (ele ment or attribute-centric schema), должна ли она возвращаться вместе с XML-данными и т. д.

информация Если вы работаете с XmlReader, учтите следующее.

Х Соединение должно оставаться открытым в течение всего времени, пока XmlReader считывает данные. Метод ExecuteXmlReader объекта SqlCommand в настоящее время не поддерживает значение поэтому вы должны явным образом закры вать соединение, закончив работу с классом чтения.

по архитектуре доступа н данным платформе Х Пример в котором показывается, как использовать XmlReader, см. в приложении XmlReader для чтения нескольких Чтение одной записи В этом сценарии вы считываете из источника данных одну запись, содер жащую заданный набор полей. Например, у вас есть идентификатор кли ента и вы хотите получить какие-либо сведения об этом клиенте, или же у вас имеется идентификатор продукта и вам нужно получить информа цию об этом продукте.

Сравнение возможных вариантов Если требуется связать данные с одной записью, считываемой из источни ка данных, можно использовать Adapter для заполнения DataSet или DataTable точно так же, как и при считывании нескольких записей. Но если функциональность не нужна, избегайте создания этих объектов.

Считывание одной записи возможно следующими способами:

использование выходных параметров хранимой процедуры;

Х применение объекта SqlDataReader.

Оба варианта позволяют обойтись без ненужных издержек, связанных с созданием набора результатов на сервере и DataSet на клиенте. Относи тельная производительность при этих вариантах зависит от уровня нагруз ки и от того, используется ли пул соединений с базой данных. Как пока зало тестирование, при наличии пула соединений и в условиях высокой нагрузки (более 200 одновременных подключений) хранимая процедура работает примерно на 30% быстрее, чем SqlDataReader.

Использование выходных параметров хранимой процедуры Используйте выходные параметры хранимой процедуры, когда:

Х нужно считать одну запись из многоуровневого Web-приложения, в котором включена поддержка пула соединений.

Дополнительная информация Х Пример кода, где показывается, как использовать выходные парамет ры хранимой процедуры, см. в приложении: Использование ных параметров хранимой процедуры для чтения одной записи.

42 Microsoft ADO.NET Применение SqIDataReader Используйте SqIDataReader, когда:

Х требуется считать не только значения данных, но и метаданные. Для получения метаданных поля метод GetSchemaTable класса чтения данных;

Х пул соединений не используется. Тогда SqIDataReader является хоро шим выбором независимо от нагрузки;

тестирование производительно сти показало, что при 200 подключенных браузерах SqIDataReader обеспечивает примерно на 20% более высокую производительность, чем хранимая процедура.

Дополнительная Х Если известно, что запрос вернет только одну запись, то при вызове метода объекта SqlCommand указывайте значение Некоторые провайдеры, например OLE DB Data Provider, используют это значение для оптимизации произ водительности. Этот провайдер при задании Row выполняет связывание с данными через интерфейс (если он доступен), а не через более ресурсоемкий При работе с SQL Server Data Provider этот аргумент ни на что не влияет.

Х Используя объект SqIDataReader. всегда считывайте выходные пара метры типизированными объекта SqIDataRea der, например или GetDecimal. Это позволит избежать лиш них преобразований типов.

Пример кода, в котором показывается, как с помощью объекта SqI DataReader считывать одну см. в приложении Использование SqIDataReader для чтения одной записи.

Чтение одного поля В этом случае требуется считать одно поле данных. Например, может по требоваться найти наименование одного продукта по идентификатору или оценку кредитоспособности (credit rating) одного клиента по его имени.

Тогда ради чтения одного поля обычно нет необходимости идти на издер жки, связанные с применением DataSet или даже DataTable.

Кроме того, вам может понадобиться просто проверить, есть ли в базе дан ных определенная: запись. Например, когда на Web-сайте регистрируется новый пользователь, вы должны проверить, нет ли в базе указанного им имени. Это частный случай чтения одного поля. Здесь достаточно вернуть булево значение.

Руководство по доступа к данным на платформе Сравнение возможных вариантов Считать одно поля из источника данных можно:

Х вызовом метода Scalar объекта содержащего хра нимую процедуру;

Х использованием выходного параметра или возвращаемого значения хранимой процедуры;

Х через объект Метод возвращает непосредственно поле данных, так как он рассчитан на запросы, возвращающие единственное значение. Этот способ требует меньше кода, чем остальные.

С точки зрения производительности, следует использовать выходной па раметр или возвращаемое значение хранимой процедуры, поскольку тес ты показали, что применение хранимых процедур обеспечивает примерно одинаковую производительность в условиях как с высокой, так и с низкой нагрузкой (от менее чем 100 подключенных браузеров до 200).

Дополнительная информация Х Если запрос, выполняемый через возвращает несколько полей и/или записей, то способы, рассмотренные в этом подразделе, Ч только первое поле первой записи, Х Пример кода, в котором показывается, как работать с ExecuteScalar, см.

в приложении Использование ExecuteScalar для чтения одного поля.

Х Пример кода, иллюстрирующий, как считывать одно поле через выход ной параметр или возвращаемое значение хранимой процедуры, см. в приложении Использование выходного параметра или возвращаемо го значения хранимой процедуры для чтения одного Х Пример кода, демонстрирующий, как с помощью объекта SqlData Reader считать одно поле, см. в приложении Использование SqlData Reader для чтения одного Подключение через брандмауэры Интернет-приложения часто приходится на подключение к SQL Server через брандмауэр (firewall). Так, ключевым элементом архи тектуры многих Web-приложений и их брандмауэров является сеть пери метра (perimeter network) (также называемая или демилитаризован ной зоной), используемая для изоляции Web-серверов, взаимодействую щих с клиентами, от внутренних сетей.

44 ADO.NET При подключении к SQL Server через брандмауэр требуется специальная настройка брандмауэра, клиента и сервера. В составе SQL Server постав ляются программы Client Network Utility и Server Network Utility, помо гающие выполнить эту настройку.

Выбор сетевой библиотеки Для упрощения настройки при подключении к SQL Server через брандма уэр используйте сетевую библиотеку TCP/IP. При установке SQL Server 2000 она устанавливается по умолчанию. Если вы работаете с одной из предыдущих версий SQL Server, убедитесь, что TCP/IP выбран в качестве сетевой библиотеки по умолчанию и на клиенте, и на сервере. Это можно сделать с помощью Client Network Utility и Server Network Utility соответ ственно.

Помимо удобства конфигурирования, библиотека TCP/IP обеспечивает следующие преимущества:

Х повышенную производительность при работе с большими объемами данных и более высокую масштабируемость;

Х исключение дополнительных проблем с безопасностью, возникающих при работе с именованными каналами.

Клиентские и серверные компьютеры нужно настроить под TCP/IP. По скольку большинство брандмауэров ограничивает набор портов, через ко торые пропускается трафик, тщательно проанализируйте, какие порты использует SQL Server.

Конфигурирование сервера По умолчанию экземпляры SQL Server прослушивают порт 1433. Однако именованным экземплярам (named instances) SQL Server 2000 номер пор та назначается динамически при первом запуске. Администратор вашей сети скорее всего не захочет открывать диапазон номеров портов на бран дмауэре, поэтому, если вы используете именованный экземпляр SQL Server в сети с брандмауэром, настройте с помощью Server Network Utility свой экземпляр на прослушивание определенного порта. Тогда админист ратор сети настроит брандмауэр чтобы тот пропускал трафик на за данный IP-адрес и порт, прослушиваемый экземпляром сервера.

Примечание Исходный порт, используемый сетевой библиотекой на клиенте, динамически выбирается из диапазона 1024-5000. Это типично клиентских приложений TCP/IP, но означает, что брандмауэр должен разрешать трафик с лю бого относящегося к этому диапазону. Подробнее о портах, используемых SQL Server, см. на сайте Microsoft Product Support Services статью INF: TCP Ports Needed to SQL Server Through a Firewall ( по архитектуре доступа к на Динамическое обнаружение именованных экземпляров Если вы изменили номер порта, SQL Server по умолча нию, настройте клиент на подключение к этому порту. Детали см. в разде ле Конфигурирование Если вы изменили номер порта для экземпляра SQL Server 2000, исполь зуемого по умолчанию, учтите, что неудачное изменение конфигурации клиента приведет к ошибке соединения. Если у вас несколько экземпля ров SQL Server, используйте последнюю версию стека доступа к данным (версию 2.6) для динамического распознавания и согласования по протоколу (User Datagram Protocol) через 1434. Хотя в среде разработки все это может функционировать нормально, вы должны понимать, что на практике брандмауэры обычно блокируют трафик согла сования по протоколу UDP.

Чтобы обойти эту ситуацию, всегда настраивайте клиент на подключение к выделенному для него порту на сервере.

Конфигурирование клиента Для подключения к SQL Server настройте клиент на использование сете вой библиотеки TCP/IP. Кроме того, убедитесь, что эта библиотека на клиентской стороне использует правильный порт назначения.

Использование сетевой библиотеки TCP/IP Клиент можно настроить с помощью Client Network Utility, поставляемой с SQL Server. В некоторых вариантах установки эта утилита может отсут ствовать на клиенте. Тогда настройте клиент на использование библиоте ки TCP/IP одним из следующих способов.

Х Укажите в строке подключения пару лимя-значение (лNetwork Строка нужна для идентификации TCP/ Примечание В случае провайдера SQL Server Data Provider параметр сете вой библиотеки dbmssocn по умолчанию.

Х Внесите изменения в реестр клиента, чтобы задать TCP/IP как биб лиотеку по умолчанию. Дополнительную информацию о конфигури ровании сетевой библиотеки SQL Server см. в Change SQL Server Default Network Library Without Using Client Network Utility Задание порта Если ваш экземпляр SQL Server настроен на прослушивание порта, отлич ного от порта по умолчанию, вы можете указать номер порта для под ключения следующими способами:

Х через Client Network Utility;

Х задав номер порта в паре лимя-значение или Data Sour указываемой в строке подключения. Используйте строку следу го шего формата:

"Data Примечание может быть IP-адресом или Для опти мальной указывайте IP-адрес, чтобы не тратить время на про смотр DNS.

Распределенные транзакции Если вы занимаетесь разработкой обслуживаемых компонентов, ющих распределенные транзакции СОМ+ и сервисные функции Microsoft DTC (Distributed Transaction Coordinator), вам может понадобиться на стройка брандмауэра на пропуск трафика DTC между разными экземпля рами DTC и между DTC и диспетчерами ресурсов (например, SQL Server).

Дополнительную информацию об открытии портов для DTC см. в INFO:

Configuring Microsoft Distributed Transaction Coordinator (DTC) to Work Through a Firewall Работа с большими двоичными объектами (BLOB) В настоящее время многим приложениям приходится иметь дело не толь ко с традиционными символьными и числовыми данными, но и с такими форматами данных, как графика и звук, и даже с еще более сложными ти пами данных вроде видео. Существует масса форматов графики, звука и видео. Однако, с точки зрения хранения, информацию таких форматов можно рассматривать как крупные массивы двоичных данных, обычно называемые большими двоичными объектами (Binary Large Ob jects, BLOB).

В SQL Server для хранения BLOB предназначены типы данных и image. Несмотря на свое название ся для хранения и текстовой информации. Например, вам может потребо ваться хранить длинные примечания произвольного размера, относящие ся к определенным записям. Для этих целей предназначены типы данных SQL Server ntext и text.

по к данным на платформе Вообще говоря, для хранения двоичных данных с размером меньше 8 Кб лучше применять тип данных а для хранения двоичных данных большего размера Ч тип image. В табл. 2 перечислены характеристики каждого из типов данных, 2. Характеристики типов данных Тип данных Размер Описание binary От 1 до 8000 байтов. При хранении Двоичные данные отводится указанный размер фиксированного размера плюс 4 байта.

varbinary От 1 до 8000 байтов. При хранении данные отводится реальный размер данных переменного размера плюс 4 байта.

image Двоичные данные переменного Большой объем двоичных размера от 0 до 2 Гб. данных размера text Данные переменного размера Символьные данные от 0 до 2 Гб.

ntext Данные переменного размера Символьные данные от 0 до 2 Гб. кодировке Unicode Где хранить BLOB SQL Server версии 7.0 и выше обеспечивает повышенную производитель ность при работе с BLOB, которые содержатся в базе данных. Одна из при чин Ч увеличение размера страницы базы данных до 8 Кб. Благодаря это му отпадает необходимость хранить текстовые или графические данные размером меньше 8 Кб в отдельной двоичной древовидной структуре стра ниц. Такие данные теперь можно хранить в одной записи. Таким образом, чтение/запись данных text, ntext или image выполняется так же быстро, как и чтение/запись символьных или двоичных строк. При превышении размера 8 Кб в запись помещается а сами данные приходится размещать в узлах древовидной структуры страниц, что неизбежно снижа ет производительность.

Дополнительную информацию о том, как добиться, чтобы данные типов text, ntext и image хранились в одной записи, см. в разделе Using Text and Image Data в SQL Server Books Online.

Широко используемый подход к работе с заключается в том, что они помещаются в файловую систему, а в поле базы данных хранится указатель на соответствующий файл всего подходит URL-ссылка). В версиях SQL Server до 7.0 хранение в файло вой системе Ч вне базы данных Ч может повысить 48 Microsoft Однако благодаря усовершенствованной поддержке BLOB в SQL Server 2000 в сочетании с поддержкой чтения и записи BLOB в ADO.NET пред почтительным подходом является хранение BLOB в самой базе данных.

Преимущества BLOB в базе данных Хранение BLOB в базе данных дает следующие преимущества.

Х Проще синхронизировать с остальными полями записи.

Х Резервное копирование BLOB выполняется вместе с резервным копи рованием всей базы данных. Наличие единственной системы хранения упрощает администрирование.

Х Вы можете обращаться к BLOB через XML, поддерживаемый SQL Server 2000. При этом возвращается закодированное в формате base представление данных в XML.

Х Над полями, содержащими символьные данные постоянной или пере менной длины (в том числе в формате Unicode), можно выполнять операции полнотекстового поиска Text Search, FTS) SQL Server.

Кроме того, можно выполнять FTS-операции над форматированными текстовыми данными, содержащимися в полях типа image, например над документами Microsoft Word или Microsoft Excel.

Запись BLOB в данных В следующем фрагменте кода как использовать ADO.NET для записи двоичных данных, считываемых из в поле SQL Server типа image.

public void string filename ) { // Считываем файл в байтовый массив fs = new filename, );

imageData = new fs.Read( 0, );

conn = new = new conn);

= filename );

Image);

Direction = // байтовый массив е поле типа image = imageData;

Руководство по архитектуре доступа к на платформе catch throw;

finally Чтение BLOB базы данных При создании объекта методом ExecuteReader для чтения записей, содержащих BLOB, значение Без этого значения класс чтения будет передавать данные с сервера на клиент по одной записи единовременно. Если запись содер жит поле BLOB, это может привести к расходу большого объема памяти.

Значение обеспечивает более тонкий контроль над чтением, так как извлекаются только при ссылке на них (например, при вызове метода GetBytes, позволяющего за давать число считываемых байтов). Все это показано в следующем фраг менте кода.

// что команда и соединение уже // Эта команда извлекает из таблицы оператором SELECT поле типа IMAGE.

SqlDataReader reader = // Получаем размер данных поля типа image;

// в качестве параметра - массива байтов передаем null long bytesize = 0, null, 0, 0);

// Выделяем память под массив байтов, предназначенный // для хранения данных поля = new long bytesread = 0;

tnt = 0;

while (bytesread < bytesize) f // - значение, определяемое приложением bytesreab curpos, imageData, curpos, curpos += chunkSize;

} // Теперь байтовый массив imageData содержит поле BLOB 50 Microsoft Применение требует строго последовательного обращения к Так, если BLOB Ч это поле 3, а данные полей 1 и 2 вам тоже нужны, то перед чтением поля 3 вы должны считать поля 1 и 2.

Транзакции Практически всем коммерческим приложениям, изменяющим данные, поддержка транзакций. Транзакции гарантируют целостность со стояния системы в рамках одного или нескольких источников данных. Это реализуется за счет свойств транзакции ACID: атомарно сти (atomicity), целостности (consistency), изоляции (isolation) и отказоу стойчивости (durability).

Рассмотрим, например, Web-приложение для автоматизации розничных продаж, заказы на товары. При поступлении каждого за каза выполняются три операции, вносящие три изменения в базу данных:

Х остаток товара уменьшается в соответствии с заказанным количеством;

Х кредит покупателя уменьшается на сумму заказа;

Х в базу данных заказов добавляется новый заказ.

Эти три операции должны атомарно Ч как единое целое.

Возможны два варианта: либо операции выполняются успешно, либо ни одна из них не Любой другой вариант означал бы нару шение целостности данных. Транзакции гарантируют соблюдение принци па все или и ряд других возможностей.

Дополнительную информацию по основам обработки транзакций см. по ссылке sactionprocessingfundamentals.asp.

Существует несколько применимых для включения механизма управления транзакциями в код доступа к данным. Каждый из этих под ходов укладывается в одну из двух основных моделей программирования.

Х Транзакции вручную. Код, использующий поддержку транзакций или пишется соответственно или в компонен тах, или в хранимых процедурах.

Х Автоматические транзакции (транзакции СОМ+). Вы добавляете в декларативные атрибуты (declarative attributes), в кото рых указываете требования объектов к транзакциям в период выпол нения. Эта модель позволяет легко настроить несколько компонентов на работу в рамках одной и той же транзакции.

по доступа к данным на платформе Оба способа годятся как для локальных транзакций (выполняемых одним диспетчером ресурсов, например SQL Server 2000), так и для распределен ных (выполняемых несколькими диспетчерами ресурсов, размещенными на удаленных компьютерах). Однако модель автоматических транзакций значительно упрощает обработку распределенных транзакций.

Автоматические транзакции (транзакции могут привлечь ваше внимание более простой моделью программирования. Особенно очевидно это преимущество в системах с многочисленными компонентами, модифи цирующими содержимое базы данных. Но во многих ситуациях эта модель приведет к дополнительным издержкам и к снижению производительности.

В этом разделе даются которые помогут выбрать наиболее подходящую для вашего приложения модель транзакций.

Выбор модели транзакций Прежде чем выбирать модель определитесь, а нужны ли вам вообще транзакции. Транзакции Ч один из самых дорогостоящих ресур сов, потребляемых серверными приложениями, и их применение без необ ходимости снижает масштабируемость приложения. Управляя транзакци ями, придерживайтесь следующих Используйте транзакции, только когда вам нужны блокировки для вы полнения группы операций и введение в действие правил ACID.

Х Выполняйте транзакции как можно быстрее, чтобы свести к миниму му время, в течение которого в базе данных действуют блокировки.

Никогда не возлагайте на клиента управление сроком действия тран закции.

Х Не используйте транзакции для отдельных SQL-операторов. SQL Server автоматически выполняет каждый оператор в рамках индивиду альной транзакции.

Автоматические транзакции и транзакции вручную Хотя автоматические транзакции несколько упрощают модель программи рования Ч особенно, когда изменения в базе данных выполняются не сколькими компонентами, Ч локальные транзакции вручную всегда зна чительно быстрее, так как не требуют взаимодействия с Microsoft DTC.

Это верно (хотя и в меньшей степени), даже если автоматические транзак ции выполняются с одним локальным диспетчером ресурсов (например, SQL Server), так как при локальных транзакциях вручную не требуется межпроцессного взаимодействия (interprocess communication, IPC) с DTC, 52 Microsoft ADO.NET Используйте ручные транзакции, когда:

Х транзакции выполняются над одной базой данных.

Используйте автоматические транзакции, когда:

Х одна транзакция охватывает несколько удаленных баз данных;

Х одна транзакция требует участия нескольких диспетчеров например базы данных и ресурсов (Message Queuing) в Win dows 2000.

Примечание Не смешивайте модели транзакций. Используйте либо одну модель, либо другую.

В производительность которых вас устраивает, есть смысл выбирать автоматические транзакции (даже над одной базой данных) для упрощения модели программирования. Автоматические транзакции упро щают выполнение несколькими компонентами являющихся ча стями одной транзакции.

Применение транзакций вручную При применении транзакций вручную код, использующий поддержку транзакций ADO.NET или пишется соответственно в ком понентах или хранимых процедурах. В большинстве случаев следует вы бирать управление транзакциями в хранимых процедурах, поскольку та кой подход обеспечивает инкапсуляцию и дает производительность, срав нимую с производительностью транзакций ADO.NET.

Выполнение транзакций вручную в ADO.NET ADO.NET подерживает объект транзакции, который можно использовать, чтобы начать новую транзакцию, а затем зафиксировать (commit) или от катить (roll back) ее. Объект транзакции сопоставляется с соединением с базой данных;

для получения этого объекта вызывается метод BeginTran объекта соединения. Вызов данного метода не означает, что после дующие команды начнут неявно выполняться в контексте транзакции. Вы должны явно связать каждую команду с транзакцией, установив свойство Transaction команды. С объектом транзакции можно несколько команд, тем самым объединив несколько операций над базой данных в одну транзакцию.

Пример использования поддержки транзакций в ADO.NET см. в приложе нии Программирование ручных транзакций по архитектуре доступа к данным на платформе информация Х По умолчанию в ручных транзакциях ADO.NET используется уровень изоляции Read Committed. Это означает, что в базе данных на время чтения из нее устанавливаются разделяемые блокировки (shared locks), но данные можно изменять до завершения транзакции. При таком уровне изоляции возможно чтение одной и той же записи по-разному reads), или появление фантомным данных (phantom data). Уровень изоляции транзакции можно изменить, присвоив свой ству ее объекта одно из значений перечислимого tionLevel.

Х Тщательно выбирайте подходящий уровень изоляции транзакций, Здесь приходится идти на компромисс между целостностью данных и производительностью. Самый высокий уровень изоляции (Serialized) обеспечивает абсолютную целостность данных за счет снижения об шей производительности системы. Более низкие уровни изоляции мо гут повысить масштабируемость приложения, но при этом возможны ошибки, связанные с нарушением целостности данных. В приложени ях, главным образом читающих и лишь изредка записывающих дан ные, лучше использовать более низкие уровни изоляции.

Х Ценную информацию о выборе подходящего уровня изоляции тран закций можно найти в книге Кэйлин Delaney) SQL Server (Microsoft Press).

Выполнение ручных транзакций в хранимых процедурах Б хранимых процедурах можно напрямую управлять ручными транзакци ями с помощью операторов Transact-SQL. Например, можно выполнить транзакционные операции в одной хранимой процедуре, используя такие операторы Transact-SQL, как BEGIN TRANSACTION, END TRANSAC и ROLLBACK TRANSACTION.

Дополнительная информация Х При необходимости в хранимой процедуре можно управлять уровнем изоляции транзакции с помощью оператора SET TRANSACTION ISO LATION LEVEL. По умолчанию в SQL Server используется уровень изоляции Read Committed. Подробнее об уровнях изоляции транзак ций SQL Server см. SQL Server Books Online (раздел Accessing and Changing Relation Data, подраздел Isolation Х Пример кода, иллюстрирующий, как выполнять изменения в рамках транзакции с помощью операторов языка Transact-SQL, см. в приложении Выполнение транзакций с 54 AD0.NET Применение автоматических транзакций Автоматические транзакции упрощают модель программирования, так как не явным образом начинать новую транзакцию или фиксировать/ отменять транзакцию. Однако главное автоматических транзакций в что они работают совместно с DTC, что позволяет ох ватывать одной транзакцией несколько распределенных источников дан ных, В крупномасштабных распределенных приложениях это очень важно.

Вы можете управлять распределенными транзакциями и вручную, напря мую обращаясь к DTC программным путем. Но автоматические транзак ции радикально облегчают задачу управления распределенными транзак циями и предназначены для компонентных систем. Например, с помощью соответствующих объявлений легко настроить несколько так, чтобы они выполняли действия, составляющие одну транзакцию.

Автоматические транзакции опираются на распреде ленных транзакций и, следовательно, могут использоваться только обслу живаемыми компонентами, т. е. компонентами, производными от класса Serviced Component.

Чтобы класс поддерживал автоматическую транзакцию, выполните следу ющие действия.

Х Наследуйте свой класс от класса Serviced Component в пространстве имен Х Определите требования класса к транзакциям с помощью атрибута Transaction. Значение, выбираемое из перечислимого Transaction Option, определяет, как класс конфигурируется в СОМ+ Catalog. Этот атрибут позволяет указать и такие свойства, как уровень изоляции транзакций и время ожидания.

Х Чтобы не приходилось явно определять результат транзакции цию или откат), пометьте методы атрибутом AutoComplete. Если метод с таким атрибутом исключение, транзакция автоматически откатывается. Имейте в виду, что у вас сохраняется возможность явно указать результат транзакции (transaction outcome). Детали см. в раз деле Определение результатов транзакций, информация Дополнительную информацию об автоматических транзакциях см. в документации Platform SDK в разделе Automatic Transactions Through Х Пример транзакционного.NET-класса см. в приложении Программи рование транзакционного класса по архитектуре доступа к данным на платформе Настройка уровней изоляции транзакций В 1.0, т. е. в версии, предоставляемой Windows 2000, используется транзакций Serialized. Это обеспечивает высшую сте пень изоляции, но за счет производительности. Общая производитель ность системы снижается, так как задействованные в транзакции диспет черы ресурсов (обычно базы данных) должны поддерживать на время транзакции блокировки и по чтению, и по записи. В течение этого време ни остальные транзакции блокируются, что может значительно ухудшить масштабируемость приложения.

В СОМ+ версии 1.5, поставляемой с Microsoft допускается настрой ка уровня изоляции транзакций в СОМ+ Catalog индивидуально для каж дого компонента. Уровень изоляции определяется параметром, сопостав ленным с корневым компонентом, участвующим в транзакции. Кроме того, у внутренних подкомпонентов, являющихся частью той же транзак ции, не должен быть более высокий уровень изоляции, чем у корневого компонента. Иначе при создании экземпляров подкомпонентов будут воз никать ошибки.

У управляемых.NET-классов атрибут Transaction поддерживает открытое свойство Isolation. Это свойство позволяет декларативно указывать опре деленный уровень изоляции, как в следующем коде.

public>

Дополнительная информация Х Подробнее о настраиваемых уровнях изоляции транзакций и других усовершенствованиях в см. статью Windows XP: Make Components More Robust with 1.5 в журнале Magazine за август 2001 г.

Определение результатов транзакций Результат автоматической транзакции регулируется флагом отмены тран закции (transaction abort flag), а также флагами целостности (consistent flags) в контексте всех компонентов одного потока тран закции. Результат транзакции определяется в момент деактивации корне вого компонента, когда управление возвращается вызывающему методу (см. рис. 5, который иллюстрирует классическую банковскую транзакцию по переводу денег).

Рис. 5. Поток транзакции и контекст Результат транзакции определяется, когда корневой объект (в данном при мере Ч объект Transfer) деактивируется и управление возвращается мето ду клиента. Если какой-либо флаг целостности в любом контексте уста новлен в false или если флаг отмены транзакции установлен в true, соот ветствующая физическая DTC отменяется.

Вы можете управлять результатом транзакции из одним из двух способов.

Х Пометить методы атрибутом AutoComplete, что позволит автома тически управлять результатом транзакции в соответствии с вашими При наличии этого атрибута Ч если метод генерирует исключение Ч флагу целостности автоматически присваивается false (что в конечном счете приводит к отмене транзакции). Если метод за вершается без генерации исключения, флагу целостности присваива ется true, и это указывает на готовность компонента к фиксации тран закции. Но фиксация транзакции не гарантируется, поскольку зависит от того, как проголосуют другие объекты, относящиеся к тому же по току транзакции.

Х Вызывать статический метод SetComplete или SetAbort класса Ч при этом флагу целостности присваивается соответственно или false.

Ошибки SQL Server с значимости выше 10 приводят к тому, что управляемый провайдер данных генерирует исключения типа SqlExcep по архитектуре доступа к платформе Если ваш метод перехватывает и обрабатывает исключение, вы дол жны вручную указать, что транзакцию нужно отменить, или, когда метод помечен как передать исключение вызывающему методу.

Методы с [AutoComplete] Для методов с атрибутом AutoComplete выполните одно из следующих действий.

Х Передайте SqlException вверх по стеку вызовов.

Х Поместите SqlException во внешнее исключение и передайте его вызы вающему методу. Например, вам может понадобиться поместить ис ключение внутрь такого типа исключений, который имеет больший смысл для вызывающего метода.

Если передать исключение дальше не удастся, объект не проголосует за отмену транзакции, несмотря на ошибку базы данных. И тогда возможна ошибочная фиксация других успешных операций, которые были выполне ны другими объектами, участвующими в том же потоке транзакции.

В следующем фрагменте кода перехватывается SqlException, которое за тем передается непосредственно вызывающему методу. В конечном счете транзакция отменяется, так как при деактивации объекта его флаг целос тности автоматически приравнивается false.

[AutoComplete] void { { // Открываем и выполняем над базой данных } catch ) sqlex );

// протоколируем параметры throw;

// снова генерируем то же исключение, чтобы // флаг целостности получил false } finally // Закрываем соединение с базой данных 58 Microsoft Методы без Б случае методов без атрибута AutoComplete вы должны:

Х вызывать Con text Set Abort в блоке чтобы при исключении проголосовать за отмену транзакции. При этом флаг целостности по лучит значение false;

Х вызывать если исключения не было, и прого таким образом за фиксацию транзакции. При этом флаг це лостности получит значение true, что совпадает с его значением по умолчанию.

Этот подход иллюстрирует следующий фрагмент кода.

void { // Открываем и выполняем операции над базой // еручнуя голосуем за фиксацию транзакции catch sqlex) { sqlex );

// протоколируем исключения // вручную голосуем за отмену транзакции // Теперь исключение обработано, и нет необходимости // передавать его вызывающему методу > finally { // Закрываем соединение с данных Примечание При наличии нескольких блоков catch проще вызвать Context в начале метода и поместить вызов ContextUtil.SetComplete в конец блока try. Тогда не придется вызывать метод ContextUtil.SetAbort в каждом блоке catch. Значение, присваиваемое флагу целостности при вызове этих иг рает роль, когда ваш метод возвращает управление.

Всегда передавайте исключения (или исключения-оболочки) обратно по стеку вызовов Ч тогда вызывающий код получает информацию о том, что транзакция закончится неудачно, и может предпринять соответствующие действия. Так, в примере с переводом денег компонент Transfer мог бы отказаться от кредитной операции, что дебетную операцию выпол не удалось.

по архитектуре доступа к данным на Если присвоить флагу целостности значение false и управление без генерации исключения, то вызывающий код не сумеет заранее узнать, что транзакция закончится неудачно. Хотя вы можете, например, вернуть значение типа Boolean или передать выходной параметр этого типа, будь те последовательны и для передачи информации об ошибочных ситуаци ях генерируйте исключения. Тогда код будет более понятным, а при обра ботке ошибок будет использоваться стандартный подход.

Загрузка данных порциями Загрузка данных порциями (data paging) Ч типичное требование, предъяв ляемое в распределенных приложениях. Например, пользователь может получить длинный список книг, который нет смысла показывать целиком за один раз;

пользователь предпочел бы выполнять такие привычные дей как просмотр следующей/предыдущей страницы списка или пере ход к его В данном разделе рассматриваются различные варианты реализации этой функциональности и влияние каждого из них на масштабируемость и про изводительность.

Сравнение возможных вариантов Для загрузки данных порциями выберите один из следующих вариантов, Х Вызов метода Fill объекта SqlDataAdapter для записи в DataSet диапа зона результатов.

Х Применение ADO через COM Interop и использование серверного курсора (server-side cursor).

Х Загрузка данных порциями вручную с помощью хранимых процедур.

Какой из вариантов окажется для вас наилучшим, зависит от:

Х требований к масштабируемости;

Х к Х полосы пропускания сети;

Х объема памяти и мощности сервера базы данных;

Х объема памяти и мощности сервера промежуточного уровня;

Х числа возвращаемых записей, которые вы хотите разбить на порции;

Х размера нужной вам порции данных.

Тестирование показало, что в широком диапазоне уровней нагрузки наи лучшую производительность обеспечивает загрузка порциями вручную с применением хранимых процедур. Однако в этом случае операции, необ для такой загрузки, выполняются на сервере. Поэтому, если зна чительная доля функциональности сайта основана на загрузке данных порциями, могут возникнуть проблемы, связанные с высокой нагрузкой на сервер. Чтобы подобрать способ, наилучший в ваших усло виях, проверьте все варианты на соответствие требованиям, предъявляе мым вами.

Ниже рассматриваются все варианты.

Применение SqIDataAdapter Как уже говорилось, объект SqIDataAdapter используется для заполнения DataSet информацией из базы данных. Один из его перегруженных мето дов Fill (показанный ниже) принимает два целочисленных индекса.

public int Fill( DataSet int startRecord, int string srcTable );

Здесь startRecord Ч индекс записи (отсчет от нуля), a maxRecords Ч число записей, которые копируются в новый DataSet, начиная со startRecord.

На внутреннем уровне SqIDataAdapter использует SqlDataReader для вы полнения запроса и возвращения результатов. SqIDataAdapter считывает результаты и создает DataSet, содержащий считанные SqlDataReader дан ные. SqIDataAdapter копирует в новый DataSet все результаты, а не толь ко maxRecords записей, начиная со startRecord, и отбрасывает все ненуж ные. Таким образом, потенциально возможна передача клиенту через сеть большого объема лишних данных. В этом и заключается основной недо статок такого подхода.

Например, если имеется 1000 записей, из которых вас интересуют записи с 900-й по 950-ю. то первые 899 все равно считываются через сеть, а затем отбрасываются. Такие издержки, возможно, окажутся минималь ными при наборах результатов малого размера, но для больших объемов данных издержки могут быть очень велики.

Применение ADO Еще один реализации порциями Ч ADO средствами СОМ. Основная идея такого подхода Ч получить доступ к сер верным курсорам, предоставляемым Recordset. Вы присва иваете свойству CursorLocation объекта Recordset значение adUseServer, Если ваш провайдер OLE DB этот параметр (как, Руководство по доступа к данным на платформе это приведет к использованию курсора на серверной сторо не. И вы с помощью этого курсора сможете перейти прямо к начальной записи, не передавая все записи клиенту через сеть.

У этого подхода два основных недостатка.

Х В большинстве случаев требуется преобразовывать записи, возвраща емые в объекте Recordset, в DataSet для использования в управляемом коде клиента. Хотя содержит перегруженный метод Fill, позволяющий преобразовать ADO-объект Recordset в этот метод не позволяет задавать начальную и конечную запись. Един ственное, что можно сделать, Ч перейти на начальную запись в объек те Recordset, перебрать все записи и вручную скопировать данные в созданный вами DataSet. Однако затрачиваемые на это усилия и, в издержки вызовов COM могут перевесить преиму меньшего сетевого трафика Ч особенно в случае DataSet не большого размера.

Х Соединение и серверный курсор остаются открытыми, пока вы извле каете с сервера нужные вам данные. Обычно курсоры, открываемые и поддерживаемые на базы данных, Ч ресурсы дорогостоящие. И хотя они позволяют увеличить производительность, есть снижения масштабируемости из-за неэкономного расходования цен ных ресурсов сервера в течение длительных периодов.

Реализация вручную Последний вариант загрузки данных порциями, рассматриваемый в этом разделе, Ч реализация этой функциональности вручную, на основе храни мых процедур. В случае таблиц, имеющих уникальный ключ, та кую хранимую процедуру сравнительно легко. В случае таблиц без уни кального ключа (вряд ли вам слишком часто попадаются такие таблицы) этот процесс усложняется.

Загрузка данных порциями из таблиц с уникальным ключом Если у таблицы есть уникальный ключ, его можно указать в блоке WHERE оператора SELECT для создания набора результатов, начинающегося с заданной записи. В сочетании с оператором SET ROWCOUNT для огра ничения размера набора результатов это обеспечивает эффективный меха низм загрузки данных порциями. Вот код хранимой процедуры, демонст рирующей такой подход.

CREATE PROCEDURE int, int AS SET SELECT FROM Products WHERE [стандартное поиска] AND > BY [сортировка, которой ProductID монотонно возрастает] GO Модуль, вызывающий эту хранимую должен просто хранить last Product ID и в промежутке между вызовами увеличивать или уменьшать его на выбранный размер порции.

Загрузка данных порциями из таблиц без уникального ключа Если у таблицы, из которой вы загружаете данные, нет уникального клю ча, лучше добавьте его Ч например, через поле со свойством identity- Это позволит задействовать описанный выше способ загрузки данных порциями.

Но и без уникального ключа можно реализовать эффективное решение для загрузки данных порциями, если у вас есть возможность формировать уникальное значение, комбинируя два поля (или более), которые являют ся частью набора результатов.

Рассмотрим следующую таблицу.

Coll Остальные \ 1 W \ X \ Y Л 1 Z А 2 W А 2 X В I W В 1 X Б этой таблице можно сформировать такое уникальное значение комбина цией полей Coll, Col2 и Со13. Это позволяет реализовать механизм загруз ки данных порциями, демонстрируемый следующей хранимой процедурой.

CREATE PROCEDURE int AS Руководство по архитектуре доступа к на SET SELECT Col3. As WHERE [стандартное условие поиска] AND > ORDER BY ASC, ASC, ASC GO Для управления загрузкой таблицы порциями клиент должен сохранять последнее значение столбца KeyField, возвращаемого хранимой процеду рой, и при новом вызове передавать его процедуре.

При реализации загрузки порциями вручную увеличивается нагрузка на сервер, зато удается избежать передачи лишних данных по сети. Тестиро вание производительности показало, что этот подход отлично работает при разных уровнях нагрузки. Однако выполнение загрузки данных пор циями на сервере может снизить масштабируемость приложений Ч все зависит от того, насколько широко такая загрузка применяется на вашем сайте. Чтобы найти оптимальный подход для своего приложения, прове ряйте его производительность в среде, где оно должно работать.

Как включить поддержку конструирования объектов класса Чтобы управляемый мог конструировать объекты, используя сервисы (COM+) Services, действуйте по следующей схеме.

Х Наследуйте свой класс от класса в пространстве имен using public>

Х Создайте переопределенную реализацию виртуального метода Const ruct. Он вызывается после конструктора, для конкрет ного языка. Единственный аргумент этого метода Ч строка инициали зации, хранящаяся в СОМ+ Catalog.

public override void Construct( string ) { // Construct вызывается сразу после // Единственный аргумент - DSN (Data Source Name), // заданное при настройке.

} Укажите для сборки строгое имя (strong name), подписав ее с помощью атрибута или У любой сборки, за регистрированной в СОМ+ Services, должно быть строгое имя. Допол нительную информацию о строгих именах сборок см. по ссылке [assembly:

Х Для поддержки динамической, или отложенной (lazy), регистрации ис пользуйте атрибуты уровня сборки и ApplicationActi и укажите соответственно имя СОМ+-приложения, хранящего компоненты сборки, и тип активации приложения. Подробнее о реги страции сборок см. по ссылке // Атрибут ApplicationName содержит // в котором будут храниться сборки [assembly :

// Атрибут определяет, // в какой процесс загружаются при активации:

// Library - компоненты выполняются в процессе создавшего их // Server - компоненты выполняются в системном процессе dllhost.exe [assembly:

В следующем фрагменте кода показан обслуживаемый компонент DataAc использующий строки инициализации для получе ния строки подключения к базе данных.

using using // Атрибут ApplicationName содержит имя // в котором будут храниться компоненты сборки Руководство по архитектуре доступа на [assembly :

// Атрибут // в какой процесс компоненты загружаются при активации:

// Library - компоненты выполняются в процессе создавшего их // Server - компоненты выполняются в системном процессе [assembly:

// Подписываем сборку. Ключевой файл с расширением snk // создается утилитой sn.exe.

[assembly:

public>

i private public { // Конструктор при создании } public override void string ) { // Construct вызывается после Единственный аргумент OSN, заданное при настройке.

= constructString;

Использование Sq I Data для чтения нескольких записей В следующем коде показывается, как использовать объект Data Adapter для выполнения команды, генерирующей DataSet или DataTable. Здесь считывается набор категорий продуктов из базы данных по ставляемой с SQL Server.

using using public DataTable { using ( conn = new SqlConnection(connectionString) ) cmd = new conn);

= da = new SqlDataAdapter( cmd );

DataTable dt = new return dt;

3 - Чтобы с помощью сгенерировать DataSet или DataTable, вы полните следующие действия.

1. Создайте объект для вызова хранимой процедуры и ука жите для него объект SqlConnection (как показано в примере) или строку подключения (в примере не показана).

2. Создайте объект и свяжите его с объектом SqlCommand.

3. Создайте объект DataTable (или DataSet). В качестве аргумента конст руктора укажите имя DataTable.

4. Вызовите метод Fill объекта SqlDataAdapter для заполнения DataSet или DataTable считанными записями.

Использование SqIData Reader для чтения нескольких записей Как это делается, показано в следующем фрагменте кода, using using public SqlDataReader SqlConnection conn = new SqlConnection( SqlCommand new conn );

= { // класс чтения.

// что соединение при закрытии объекта // класса чтения.

return{ ) );

} catch < throw;

// список продуктов, консоль private void { reader = while Руководство по архитектуре доступа к данным на );

} // закрывается и соединение, так как при генерации // класса было задано значение Чтобы считать несколько записей с помощью выполните следующие действия.

1. Создайте объект для выполнения хранимой процедуры и свяжите его с объектом 2. Откройте соединение.

3. Сгенерируйте объект SqlDataReader, вызвав метод ExecuteReader объекта SqlCommand.

4. Для чтения данных из потока вызовите метод Read объекта SqlData Reader и извлеките значения полей с помощью типизированных мето (например, или GetString).

5. Закончив работу с классом чтения, вызовите его метод Close.

Использование для чтения нескольких записей Объект SqlCommand можно использовать для генерации объекта XmlRea der, предоставляющего доступ к потоку XML-данных в направлении толь ко вперед. Команда (обычно хранимая процедура) должна сгенерировать набор результатов в формате Для этого в SQL Server 2000 обычно применяется оператор SELECT с блоком FOR XML. Этот подход показан в следующем фрагменте кода.

public void { SqlConnection conn = new SqlCommand = new conn );

= try I = ( ) { if ( == "PRODUCTS" ) I string = Microsoft += += strOutput );

} catch { throw;

} finally { } :

Предыдущий код вызывает следующую хранимую процедуру.

PROCEDURE AS SELECT * FROM PRODUCTS FOR XML AUTO GO Чтобы считать XML с помощью XmlReader, выполните следующее.

1. Создайте объект для вызова хранимой процедуры, гене рирующей набор результатов в формате (например, с помощью блока FOR XML оператора SELECT). Свяжите объект с объектом соединения.

2. Вызовите метод объекта SqlCommand и присвойте результаты объекту XmlTextReader с поддержкой перемещения по за писям в только вперед. Это самый быстродействующий тип объекта который следует использовать, если вам не нужно проверять возвращаемые XML-данные на допустимость.

3. Считайте данные методом Read объекта XmlTextReader.

Использование выходных параметров хранимой процедуры для чтения одной записи Для чтения одной записи можно вызвать хранимую процедуру, возвраща ющую считанные поля этой записи как именованные выходные парамет ры. В приведенном ниже фрагменте кода используется хранимая процеду ра, считывающая наименование и цену определенного продукта из табли цы Products базы данных Руководство по архитектуре доступа к на платформе void int ProductID, out string out decimal ) SqlConnection conn new // Создаем команды для хранимой процедуры cmd = conn );

= // Указываем параметры хранимой процедуры:

// int INPUT // OUTPUT // money OUTPUT // Для выходных параметров нужно явно задавать направление = ProductID );

= SqlParameter paramProdName = Parameters. Add( = SqlParameter );

= try < // Для выполнения команды используйте // Хотя этот метод не возвращает записей, происходит заполнение // выходных параметров (и возможно, возвращаемых // Возвращаем выходные параметры хранимой процедуры ProductName = UnitPrice = } catch ( throw;

} finally { Чтобы считать одну запись через выходные параметры хранимой процеду ры, выполните следующее.

1. Создайте объект SqlCommand и свяжите его с объектом SqlConnection.

70 ADO.NET 2. Задайте параметры хранимой процедуры, вызывая метод Add набора Parameters объекта По умолчанию параметры считают ся входными, поэтому для каждого выходного параметра нужно явно задать направление.

Примечание Хороший стиль Ч указывать направление для всех в том числе и для входных.

3. Откройте соединение.

4. Вызовите метод Query объекта При этом за полняются выходные параметры (и, возможно, возвращаемое значение).

5. Считайте значения выходных параметров через свойство Value объек тов Parameter.

6. Закройте соединение.

Предыдущий фрагмент кода вызывает следующую хранимую процедуру.

CREATE PROCEDURE int, OUTPUT, money OUTPUT AS SELECT = ProductName, = UnitPrtce FROM Products WHERE ProductID = GO Использование для чтения одной записи Объект SqIDataReader можно использовать для чтения одной записи, в частности для чтения значений определенных полей из возвращаемого потока данных. Вот пример.

void int out string ProductName, out decimal UnitPrice ) { conn = new SqlConnection( // для выполнения хранимой процедуры SqlCommand = new conn );

= // Указываем параметры хранимой процедуры // int INPUT = ProductID );

= по архитектуре доступа к на платформе try { SqlDataReader reader = // читаем единственную // выходные из полученного потока данных = UnitPrice = } catch { } { Для чтения одной записи с помощью объекта SqlDataReader выполните следующие действия.

1. Создайте объект SqlCommand.

2. Откройте соединение.

3. Вызовите метод объекта SqlCommand, чтобы получить объект SqlDataReader.

4. Считайте выходные параметры типизированными методами-аксессора ми объекта SqlDataReader (в данном случае Ч и В предыдущем фрагменте кода вызывается следующая хранимая процедура, CREATE AS SELECT ProductName, UnitPrice FROM Products WHERE = GO Использование для чтения одного поля Метод ExecuteScalar предназначен для запросов, возвращающих только одно значение. Если запрос возвращает несколько полей и/или записей, ExecuteScalar вернет для этого запроса лишь первое поле первой записи.

В следующем фрагменте кода показывается, как найти наименование про дукта по идентификатору.

72 ADG.NET void int out string ) { SqlConnection conn = new SqlConnection( cmd new conn );

= ProductID );

try { = ExecuteScalar();

} catch { throw;

} finally { } Чтобы считать одно поле через выполните следующие действия.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

2. Откройте соединение.

3. Вызовите метод ExecuteScalar. Обратите внимание, что он возвращает значение первого считанного поля как объектный тип, и вы должны привести его к нужному типу.

4. Закройте соединение.

В приведенном выше фрагменте кода используется эта хранимая процедура.

CREATE PROCEDURE LookupProductNameScalar int AS SELECT TOP 1 ProductName Products WHERE ProductID = GO по архитектуре доступа к на Использование возвращаемого значения или выходного параметра хранимой процедуры для чтения одного поля Вы можете получить единственное значение через возвращаемое значение или выходной параметр хранимой процедуры. В следующем коде демон стрируется вариант с выходным параметром.

void int ProductID, out ProductName ) { SqlConnection conn = new = new conn );

= SqlParameter = ProductID );

= SqlParameter = = try ProductName = } catch { throw;

} finally { } I Для чтения единственного значения из выходного параметра хранимой процедуры выполните следующее.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

2. Задайте все входные параметры и единственный выходной параметр, до бавляя объекты SqlParameter в набор Parameters объекта SqlCommand.

3. Откройте соединение.

4. Вызовите метод объекта SqlCommand.

5. Закройте соединение.

6. Извлеките выходное значение из свойства Value объекта SqlParameter.

В приведенном выше фрагменте кода используется следующая хранимая процедура.

CREATE OUTPUT AS SELECT = FROM Products WHERE = GO Следующий код иллюстрирует, как воспользоваться значе нием хранимой процедуры, чтобы проверить наличие определенной запи си. С точки зрения программирования, это аналогично применению вы ходных параметров Ч с тем исключением, что вы должны явно присвоить объекту значение Value.

ProductID ) conn = new = new conn );

= ProductID );

SqlParameter paramRet = = try { \ catch { throw;

} finally } return == 1;

Чтобы проверить наличие определенной записи, выполните следующие действия.

1. Создайте объект SqlCommand для вызова хранимой процедуры.

Руководство по архитектуре доступа к данным на платформе 2. Задайте входной параметр, содержащий значение первичного ключа той записи, наличие которой вы хотите проверить.

3. Задайте единственный параметр для значения. Добавь те объект SqlParameter в набор Parameters объекта и ус тановите его направление как 4. Откройте соединение.

5. Вызовите метод объекта SqlCommand.

6. Закройте соединение.

7. Получите значение свойства Value объекта SqlParameter.

В показанном выше фрагменте кода используется следующая хранимая процедура.

CREATE PROCEDURE int AS IF EXISTSC SELECT FROM Products WHERE ProductID ) return GO Использование SqIDataReader для чтения одного поля Объект SqIDataReader позволяет получать единственное выходное значе ние методом ExecuteReader объекта команды. Это требует чуть больше кодирования, чем в предыдущих случаях, поскольку вы должны вызвать метод Read объекта SqIDataReader, а затем считать требуемое значение с помощью одного из класса чтения. Использование объекта SqIDataReader демонстрирует следующий код, int ProductID ) { SqlConnection = new SqlConnection( = new conn );

= ProductID };

cmd. Direction = ( SqIDataReader reader = 76 Microsoft ADO.NET bRecordExists = > 0;

return bRecordExists;

catch throw;

finally В приведенном фрагменте кода используется следующая хранимая процедура.

CREATE PROCEDURE int AS SELECT FROM Products WHERE = GO Программирование ручных транзакций ADO.NET Ниже показано, как использовать поддержку транзакций в SQL Server Data Provider, чтобы с помощью транзакции защитить перевод де нежных средств. Перевод средств выполняется между двумя счетами, на ходящимися в одной базе данных.

public void string string decimal amount ) { using ( conn = new SqlConnection( ) ) { SqlCommand cmdCredit = new conn );

= new toAccount) );

new ));

SqlCommand = conn );

= CommandType.StoredProcedure;

new fromAccount) );

new amount ));

// Начинаем новую транзакцию по архитектуре к SqlTransaction trans = ) { // с этой транзакцией два объекта команды = trans;

= trans;

try { // Обе команды (для дебета и кредита) выполнены успешно trans.

} catch( Exception ex ) { // потерпела неудачу trans.

// Протоколируем параметры исключении...

throw ex;

Выполнение транзакций с помощью Transact-SQL Вот как выполнить операцию по переводу денег с ис пользованием хранимой процедуры, написанной на Transact-SQL.

CREATE PROCEDURE money AS BEGIN TRANSACTION - ВЫПОЛНЯЕМ ОПЕРАЦИЮ ПО ДЕБЕТУ UPDATE SET = Balance WHERE = IF BEGIN From Account 11, 1) GOTO ABORT END DECLARE money SELECT = Balance FROM ACCOUNTS WHERE = IF < BEGIN 1) GOTO ABORT END - ОПЕРАЦИЮ ПО КРЕДИТУ UPDATE Accounts SET = Balance + WHERE = IF = BEGIN To Account 11, 1) GOTO ABORT END COMMIT TRANSACTION RETURN ABORT:

ROLLBACK TRANSACTION GO В этой хранимой процедуре для управления транзакцией вручную исполь зуются операторы BEGIN TRANSACTION, COMMIT TRANSACTION и ROLLBACK TRANSACTION.

Программирование В следующем примере показано три управляемых обслуживаемых класса, сконфигурированных для автоматических транзакций. Каждый класс помечен атрибутом Transaction, значение которого определяет, сле дует ли создавать новый поток транзакции или объект должен участвовать в потоке транзакции объекта. Операция по переводу денег выполняется при взаимодействии этих компонентов. Класс Transfer поме чен атрибутом транзакции а классы Debit и Credit Ч атрибу том Required, В итоге при выполнении все три объекта участвуют в одной и той же транзакции.

using System;

using public>

< public void string string fromAccount, decimal amount } { try // по дебету Debit debit = new Debit();

fromAccount, amount );

// Выполняем операцию по кредиту по доступа к на платформе Credit credit = new toAccount, amount );

} catch( SqlException sqlex ) { // Обрабатываем исключение и его параметры.

// в оболочку и передаем дальше.

throw new TransferException( "Transfer Failure", sqlex );

public Credit : ServicedComponent { public void string account, decimal amount ) { SqlConnection conn = new SqlConnection( Integrated conn );

= new account) );

new amount ));

} catch (SqlException sqlex) { // Протоколируем параметры исключения throw;

// исключение дальше public>

= new account) );

new amount ));

try { 80 ADO.NET catch (SqlException // параметры исключения throw;

// передаем исключение обратно Благодарности Выражаем признательность всем, кто участвовал в подготовке этой статьи:

Bill Vaughn, Mike Doug Rothaus, Kevin White, David Schleifer, Graeme Malcolm (Content Master), Bernard Chen (Sapient), Matt Drucker (Turner Broadcasting) и Steve Kirk.

Вопросы? Замечания? С авторами статьи можно связаться (на английском языке) по адресу Хотите освоить платформу и задействовать всю ее мощь? Для на опытом лучше всего поработать бок о бок с экспертами из технологи ческих центров Microsoft. Дополнительную информацию можно получить по ссылке Джонни Папа ADO.NET: концепции и В этой статье подробно рассматривается концепция отделения доступа к данным через ADO.NET от бизнес-логики и как добиться этого на практике. Автор реализует компонент, абстрагирующий доступ к данным, на двух и Visual Basic С появлением Microsoft разработчики готовятся воспользоваться такими достоинствами этой платформы, как улучшенные возможности доступа к данным. Хотя и СОМ, и Visual Basic 6.0 будут широко приме еще долгие годы, уже сейчас заметен большой интерес к переходу на Чаше всего меня спрашивают, чем отличается раз работка с использованием ADO.NET от ADO 2л и как логику доступа к данным через ADO в отдельный компонент. Эти вопросы мы и рассмотрим.

Архитектура ADO.NET предоставляет возможности манипулиро вания данными в программах на С#, Visual Basic и других языках. В предыдущей статье из этой рубрики я продемонст рировал методику инкапсуляции компонента к данным с исполь зованием ADO 1.x и Basic 6.0. Разумеется, концепция отделения доступа к данным от бизнес-логики относится не только к Visual Basic 6.0.

Аналогичные подходы применяются при разработке для и в этой статье мы начнем разбираться с ADO.NET и ее влиянием на в 2002. № Ч Прим. изд.

82 Microsoft ADO.NET рование под Я покажу, как создать сервис для доступа к данным че рез ADO.NET, отделенный от бизнес-логики, реализуемой посредством В примерах, которые мы рассмотрим, три основных составляющих:

Х Web-форма, содержащая серверные элементы управления, такие как asp:DataGrid, для отображения данных;

Х получающий данные от сервиса доступа к данным и заполняющий ими элементы на Web-форме;

Х исходный код, в котором определяется собственное пространство имен и классы, реализующие сервис доступа к данным.

Сейчас много спорят, какой Хотя спроек тирована так, что ни один конкретный язык не имеет существенных имуществ перед другими, в каждом отдельном случае неплохо определить ся с этим вопросом. Между тем, пока дискуссии насчет языков продолжа ются, я приведу примеры как на Visual так и на С#. При этом не что сборки, созданные с использованием этих языков, могут взаимодействовать друг с другом. Иначе говоря, можно создать сборку на С# и обращаться к ней из класса, написанного на Visual Basic Web-форма Начнем с простой Web-формы, представляющей пользовательский интер фейс приложения. Код формы WebForm.aspx содержит два серверных элемента управления DataGrid, которые будут заполнены данными из двух разных DataSet (рис. 1). DataGrid позволяет представлять набор данных как HTML-таблицу. (О DataGrid подробно рассказывает Дино Эспозито в рубрике Edge в апрельском, май ском и июньском номерах за 2001 г.) На моей Web форме два элемента DataGrid: grdSql (заполняется SQL-оператором с ис пользованием моего сервиса доступа к данным) и grdProc (заполняется хранимой процедурой с применением того же сервиса).

Заметьте: Web-форма на рис. 1 содержит только директивы, HTML и сер верные элементы управления. Собственно кода здесь нет. В Frame work код, реализующий можно отделить от самого интерфейса. Иначе говоря, HTML и серверные элементы управления могут быть внутри Web-формы, а весь код, взаимодействующий с UI, может храниться в от дельном файле класса. Это альтернатива стандартному подходу в ASP, где код и интерфейс смешаны, так что управлять проектом теперь гораздо проще.

ADQ.NET: концепции и реализация Рис. 1. Web-форма из Visual Basic of Executing a SQL a rst f -name />

of Executing a Stored and Filling a />