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

том1 а л ь м а н а х программиста Тематический сборник материалов MSDN Library и MSDN Magazine Microsoft ADO.NET

licrosoft SQL Server Доступ к данны из приложений КРУССШ Microsoft* Microsoft5 TM Составитель Ю. Е. Купцевич Москва 2003 fii. P У С С I А 1 P E 1 i К - 1 1 УДК 004.45 ББК 32.973.26-018.2 А57 А57 Альманах программиста, том I: Microsoft ADO.NET, Microsoft SQL Server, доступ к данным из пршюжений/Сост. Ю.Е.Купцевич. Ч М.: Издательско-торговый дом Русская Редакция, 2003. - 400 с.: ил.

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

УДК 004. ББК 32.973.26-018..NET. ActiveSync, ActiveX, IntelliSense. JScript, Microsoft, Microsoft Press, MSDN, Outlook. SQL Server, VBScript, Visual Basic, Visual C+-. Visual J++, Visual Studio, Win32, Windows и Windows NT ЯНЛЯЕОТСЯ либо охраняемыми товарными знаками, либо товарными знаками корпорации Microsoft в США и/или других странах. NT Ч товар ный знак компании Northern Telecom Limited. Все другие товарные знаки являются собственностью соответствующих фирм.

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

Microsoft Corporation и CMP Media LLC, 200:i ) ISBN 5-7502-0234-8 0 ИТД Русская Редакция, Альманах программиста, том 1:

Microsoft ADO.NET, Microsoft SQL Server, доступ к данным из приложений Составитель Ю. Е. Купцевич Технический редактор Л. А, Памчук Компьютерная верстка и дизайн В. Б. Хилъченко Дизайнер обложки Е. В. Козлова Главный редактор А. И. Козлов Подготовлено к печати издательством Русская Редакция 12КЖ7, Москва, ул. Заречная, д. тел.: (095) 112-0571. тел./факс: {091)) 145-4519,e-mail: info@rusedit.ru, И Подписано в печать 14.03.03 г. Тираж 5000 экз. Формат 70x100/16. Физ. п. л. Отпечатано в ОАО Типография Новости* 1(17105. Москва, ул. Фр. Энгельса, Ad Оглавление Microsoft ADO.NET Алекс Макмен. Крис Брукс, Стив Басби, Эд Джезирски Руководство по архитектуре доступа к данным на платформе.NET Джонни Папа Доступ к данным ADO.NET: концепции и реализация Боб Бьючмин ADO.NET Разработка собственных провайдеров данных для.NET Data Access Framework Джонни Папа Доступ к данным Выражения в ADO.NET Дино Эспозито На переднем крае Двоичная сериализация ADO.NET-объектов Прийя Дхаван Управление транзакциями Разработка распределенных приложений в.NET Microsoft SQL Server Джонни Папа Доступ к данным Пять способов подстегнуть производительность SQL Алок Мехта и Дэниел Уильяме Сценарии в SQL Преобразование данных и предоставление отчетов SQL Server 2000 через VBScript-интерфейсы Франческо Балена SQLServer и DMO Автоматизация выполнения административных задач в SQL Server Дэйв Грундгейгер, Энсон Голдэйд и Вэрон Фугман SQL и XML Вызов хранимых процедур и получение их результатов через Web Оглавление Марк Браун и Дэвид Менье Мобильность Компактные и надежные приложения на основе SQL Server СЕ 2.0 и.NET Compact Framework Марк Браун SQLServer Доставка информации в реальном времени с применением Notification Services Доступ к данным из приложений Атиф Азиз.NET Reflection Динамическое связывание уровня данных с хранимыми процедурами и командами SQL Майкл Говард и Кит Браун Советы по защите Десять лучших приемов защиты кода, о которых должен знать каждый разработчик Джонни Папа Доступ к данным Объекты PataRelation в ADO.NET Прийя Дхаван Разработка распределенных приложений в.NET Операции над данными с иерархической структурой Джонни Папа Доступ к данным Модификация приложения для отображения данных в Web Джонни Папа Доступ к данным ADO и XML: создание уровня доступа к данным на основе компонента DataManager Марк Герлах Spider в.NET Сбор информации от Web-сайтов и каталогов с применением Visual Basic.NET и ADO.NET Кен Спенсер Основы и тонкости Управление транзакциями между компонентами.NET..... От составителя Уважаемый читатель!

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

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

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

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

Х Технология доступа к данным Microsoft ADO.NET. Подробно опи сывается архитектура доступа к данным на платформе.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 через механизм.NET Reflection, использованию ADO.NET-обьек тов DataRelation, операциям над иерархическими наборами строк, со зданию уровня доступа к данным через компонент DataManager (для тех, кто пока работает с ADO), сбору информации от Web-сайтов и каталогов с применением ADO.NET, реализации универсального сред ства отображения данных на основе СОМ+ или MTS (Microsoft Trans action Services), выполнению локальных и распределенных транзакций в.NET-приложениях, созданию транзакций в ADO.NET.

Исходный код для статей альманаха можно скачать по ссылке, указанной в конкретной статье, или в полном комплекте (для всех статей альманаха) с Web-сайта издательства Русская Редакция* по ссылке www.rusedit.ru/ download/code_ap I.zip.

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

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

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

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

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

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

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

* Alex Mackman, Chris Brooks, Steve Busby, and Ed Jezierski.NET Data Access Architecture Guide//MSDN Library. Microsoft Corporation. 2001. October. - Прим. изд.

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

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

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

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

Однако в ADO.NET появился ряд серьезных изменений и новшеств, выз ванных слабосвязанной природой Web-приложений и тем фактом, что по сути они отсоединены от баз данных. Сравнение ADO и ADO.NET см. в статье ADO.NET for the ADO Programmer в MSDN.

Руководство по архитектуре доступа к данным на платформе -NET.

Одно из ключевых новшеств ADO.NET Ч замена ADO-объекта Recordset комбинацией объектов DataTable, DataSet, DataAdapter и DataReader.

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

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

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

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

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

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

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

Остальные провайдеры данных.NET в настоящее время находятся в состоянии бета-тестирования.

Х ODBC.NET Data Provider. В данный момент доступна для загрузки первая бета-версия. Этот провайдер обеспечивает родной доступ к ODBC-драйверам так же, как и OLE DB.NET Data Provider к род ным провайдерам OLE DB. Получить дополнительную информацию об ODBC.NET и скачать бета-версию можно по ссылке microsoft, com/downloads/default, asp? URL=/code/sample.asp?url=/ MSDN-FILES/027/001/668/msdncompositedoc.xml.

Х Управляемый провайдер для считывания XML из SQL Server 2000.

XML for SQL Server Web update 2 (в настоящий момент проходит бета тестирование) включает, помимо всего прочего, управляемый провай дер, предназначенный специально для считывания XML из SQL Server 2000. Дополнительную информацию об этом обновлении см. по ссыл ке msdn-files/027/001/602/msdncompositedoc.xml.

Структура пространств имен Типы (классы, структуры, перечислимые и т.д.), связанные с каждым из провайдеров данных.NET, находятся в собственных пространствах имен.

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

Х System.Data.Оdbc. Содержит типы ODBC.NET Data Provider.

Х System.Data. Содержит типы, независимые от провайдеров, например DataSet H'DataTable.

Для каждого из провайдеров в его пространстве имен содержатся реали зации объектов Connection, Command, DataReader и DataAdapter. Имена реализаций объектов из пространства имен SqlClient начинаются с пре фикса

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

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

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

по ссылке implementingnetdataprovider.asp.

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

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

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

Microsoft ADO,NET Неуправляемые клиенты Управляемые клиенты.NET AOO.NET DataSet QLfDB ЛЕТ Data Pimlder E SQL Server.NET DataReader TDS Oracle, SQL Server SQL Server Access и др. версии 6. версии 7. и ниже и выше Рис. 2. Стек доступа к данным Используйте хранимые процедуры, а не встраиваемые операторы SQL по следующим причинам.

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

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

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

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

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

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

// Объект Command настраивается через аргументы конструктора SqlCommand cmd = new SqlCommand( "SELECT - FROM PRODUCTS", conn );

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

cmd.Connection = conn;

cmd.CommandText = "SELECT * FROM PRODUCTS";

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

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

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

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

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

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

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

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

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

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

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

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

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

Общее описание создания пулов соединений в MDAC (Microsoft Data Access Components) см. по ссылке dnmdac/html/pooling2.asp.

Провайдеры данных ADO. NET обеспечивают незаметное для пользовате ля создание пула соединений, точный механизм которого зависит от про Руководство по архитектуре доступа к данным на платформе.(ЧЕТ вайдера. В этом разделе рассматривается создание пула соединений для следующих провайдеров:

Х SQ.L Server.NET Data Provider ( us/dnbda/html/#daag_sqlserverproviderpooling);

Х OLE DB.NET Data Provider ( dnbda/html/#daag_oledbproviderpooling).

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

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

"Server=(local);

Integrated Security=SSPI;

Database=Northwind;

Max Pool Slze=75;

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

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

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

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

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

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

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

Полный список ключевых слов, используемых в строках подключений при создании пула соединения, см. по ссылке rary/en-us/cpguidnf/html/cpconconnectionpoolingforsqlservernetdataprovi der.asp.

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

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

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

SqlConnection conn = new SqlConnection( "Inteflrated Security=SSPI;

Database=Northwind");

conn.0pen();

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

Database=Northwlnd");

conn.0pen();

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

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

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

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

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

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

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

Подробнее о создании ресурсного пула соединений OLE DB см. в MSDN руководство OLE DB Programmer's Reference (глава 19 OLE DB Ser vices, раздел Resource Pooling).

Управление пулами соединений с помощью объектов пула Как разработчик для Windows DNA, вы можете отключить создание ресур сного пула OLE DB и/или создание пула соединений ODBC и использо вать в качестве пула соединений к базе данных объектный пул СОМ+. На то могут быть две основных причины:

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

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

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

Microsoft ADO.NET Если вы используете OLE DB.NET Data Provider, а также хотите добить ся удобства настройки и повышения производительности, стоит подумать о поддержке объектного пула СОМ+. Если в этих целях вы разрабатывае те объект пула (pooled object), отключите в OLE DB ресурсный пул и ав томатическое включение ресурсов в транзакции (например, указав в строке подключения OLE DB Services=-4). В своей реализации объекта пула вам придется самостоятельно управлять включением ресурсов в транзакции.

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

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

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

ка Profiler.

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

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

3.

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

4.

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

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

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

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

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

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

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

3.

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

4.

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

5.

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

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

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

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

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

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

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

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

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

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

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

22 Microsoft ADO.NET Избегайте олицетворения на промежуточном уровне Аутентификация через Windows требует для доступа к базе данных учет ную запись 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 см. в разделе Подключение через брандмауэры далее в этом документе.

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

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

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

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

Х файле конфигурации приложения ( en-us/dnbda/html/#daag_usingxmlappconfigfiles), например в файле Web.config Web-приложения ASP.NET;

Х UDL-файле (Universal Data Link) ( en-us/dnbda/html/#daag_usingudlfiles) Ч этот способ годится только для OLE DB.NET Data Provider;

Х реестре Windows ( html/#daag_usingwindows registry);

Х собственном файле ( html/#daag_usingcustomfiles);

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

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

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

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

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

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

Odd key="DBConnStr" value="server=(local);

Integrated Security=SSPI;

database=northwind"/> Примечание Элемент следует за элементом , а не непосредственно за .

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

Х Простота в программном доступе. Благодаря свойству AppSettings класса ConfigurationSettings считать строку подключения к базе дан ных в период выполнения приложения очень легко.

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

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

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

using System.Configuration;

private string GetDBaseConnectionStringO { return ConfigurationSettings.AppSettings["DBConnStr"];

) Х Дополнительную информацию о конфигурировании приложений.NET Framework см. по ссылке cpguidnf/html/cpconconfiguringnetframeworkapplications.asp.

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

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

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

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

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

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

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

Х Работая с Web-приложениями, убедитесь, что DDL-файл не находит ся в виртуальном каталоге, Ч в ином случае появится потенциальная возможность скачивания этого файла через Web.

Х Дополнительную информацию об этих и других особенностях ASP.NET, связанных с безопасностью, см. по ссылке library /en-us/dnbda/html/authaspdotnet.asp.

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

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

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

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

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

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

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

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

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

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

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

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

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

28 Microsoft ADO. NET Дополнительная информация Х Подробнее о том, как настраивать.NET-класс на конструирование объ екта (object construction), см. в приложении Как включить поддержку конструирования объектов.NET-класса ( library /en-us/dnbda/html/#daag_howtoenableobjectconstruction).

Х Подробнее о разработке обслуживаемых компонентов см. по ссылке servicedcomponents.asp.

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

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

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

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

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

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

public void DoSomeWorkO { SqlConnection conn = new SqlConnection{connectionString);

SqlCommand cmd == new SqlCommandC'CommandProc", conn );

cmd.CommandType = CommandType.StoredProcedure;

try conn.0pen();

cmd. ExecuteNonQue ry( ) ;

Руководство по архитектуре доступа л данным на платформе.NET catch (Exception e) // Обрабатываем и протоколируем ошибку finally conn.CloseO;

} Второй фрагмент кода иллюстрирует альтернативный подход с примене нием оператора using языка С#. Обратите.внимание, что в Visual Basic.NET нет оператора using или его эквивалента.

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

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

fcmd.CommandType = CommandType.StoredProcedure;

conn.0pen();

cmd.ExecuteQue ry();

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

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

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

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

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

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

Например, SQL Server.NET Data Provider генерирует объекты SqlExcep tion, когда в SQL Server возникает ошибочная ситуация. Аналогично OLE DB.NET Data Provider генерирует исключения типа OleDbException с параметрами, предоставленными нижележащим провайдером OLE DB.

На рис. 3 показана иерархия исключений, генерируемых провайдерами данных.NET Заметьте, что класс OleDbException наследуется от External Exception Ч базового класса для всех исключений COM Interop. Свойство ErrorCode этого объекта содержит COM HRESULT, сгенерированный OLE DB.

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

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

;

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

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

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

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

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

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

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

using System.Data;

using System.Data.SqlCllent;

using System.Diagnostics;

// Метод, предоставляемый компонентом DAL (Data Access Layer) public string GetProductNameC int ProductID ) { SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=northwind");

// Весь код доступа к данным помещаем в блок try try I conn.OpenO;

SqlCommand cmd = new SqlCommand("LookupProductName", conn );

cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add("@ProductID", ProductID );

SqlParameter paramPN = cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );

paramPN.Direction = ParameterDirection.Output;

cmd.ExecuteNonQueryO;

// Блок finally выполняется до того, как метод возвращает управление return paramPN.Value.ToStringO;

32 Microsoft ADO.NET catch (SqlException sqlex) { // Обрабатываем исключение, возникшее при доступе к данным, // и протоколируем сведения о нем LogException(sqlex);

// Включаем текущее исключение в более подходящее внешнее // и повторно генерируем исключение throw new DALException( "Unknown ProductID: " + ProductlO.ToStringO, sqlex );

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

throw ex;

} finally I conn.Close();

// соединение будет закрыто в любом случае \ // Вспомогательна процедура, заносящая сведения из SqlException // в журнал событий приложения private void LogExceptionC SqlException sqlex ) { EventLog el = new EventLogO;

el. Source = "CustomAppLog";

string strMessage;

strMessage = "Exception Number : " +Х sqlex. Number + "(" + sqlex. Message + ") has occurred";

el.WriteEntry< strMessage );

foreach (SqlError sqle in sqlex. Errors) { strMessage = "Message: " + sqle. Message + " Number: " + sqle. Number Х+ Procedure: " + sqle. Procedure + " Server: " + sqle. Server + " Source: " + sqle. Source + " State: " + sqle. State + " Severity: " + sqle.>

el.Writentry( strMessage );

) !

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

Дополнительная информация Х Полный список членов класса SqlException см. по ссылке msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemDataSqlClient Sql Except ion Members Topi c. asp.

Х Дополнительные сведения о разработке собственных исключений, протоколировании.NET-исключений и включении их в оболочки, а также о различных подходах к передаче исключений см. по ссылке /default. asp?url=/library/en-us/dnb da/html/exceptdotnet.asp.

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

Самый простой способ вызова функции RAISERROR Ч передать в каче стве первого параметра текст сообщения, а затем задать параметры, опре деляющие уровень значимости ошибки (severity) и состояние:

RAISERROFK 'Unknown Product ID: X s ', 16, 1, @ProductID ) Здесь параметр подстановки используется, чтобы вернуть в сообщении об ошибке текущий идентификатор некоего продукта. Второй параметр уровень значимости, а третий Ч состояние сообщения.

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

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

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

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

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

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

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

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

public delegate void SqlInfoMessageEventHandler( object sender, SqllnfoMessageEventArgs e );

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

public string GetProductName{ int ProductID ) ( SqlConnection conn = new SqlConnectionf "serve r=(local);

Integrated Security=SSPI;

database=northwind");

try i // Регистрируем обработчик события для информационного сообщения conn. InfoMessage += new SqlInfoMessageEventHandler( MessageEventHandler };

conn.0pen();

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

Х finally < conn.CloseO;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Применение DataSet Используйте DataSet, заполняемый объектом SqlDataAdapter, если:

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

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

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

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

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

Дополнительная информация Используя SqlDataAdapter для генерации DataSet или DataTable. имейте в виду следующее.

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

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

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

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

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

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

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

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

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

Х вам нужно упростить и оптимизировать доступ к данным;

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

Подробнее об обработке BLOB см. в разделе этого документа Работа с большими двоичными объектами (BLOB).

Дополнительная информация При использовании SqIDataReader имейте в виду следующее.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Х через объект SqlDataReader.

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

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

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

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

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

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

Microsoft 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 for Communication to SQL Server Through a Firewall ( soft.com/isapi/gosu ppoit.asp?Target=/support/kb/articles/Q287/9/32.ASP).

Руководство по архитектуре доступа к данные на платформе.NET Динамическое обнаружение именованных экземпляров Если вы изменили номер порта, прослушиваемого SQL Server по умолча нию, настройте клиент на подключение к этому порту. Детали см. в разде ле Конфигурирование клиента.

Если вы изменили номер порта для экземпляра SQL Server 2000, исполь зуемого по умолчанию, учтите, что неудачное изменение конфигурации клиента приведет к ошибке соединения. Если у вас несколько экземпля ров SQL Server, используйте последнюю версию стека доступа к данным MDAC (версию 2.6) для динамического распознавания и согласования по протоколу UDP (User Datagram Protocol) через UDP-порт 1434. Хотя в среде разработки все это может функционировать нормально, вы должны понимать, что на практике брандмауэры обычно блокируют трафик согла сования по протоколу UDP.

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

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

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

Х Укажите в строке подключения пару лимя-значение (лNetwork Lib rary=dbmssocn). Строка dbmssocn нужна для идентификации TCP/ 1Р-сокетов.

Примечание В случае провайдера SQL Server.NET Data Provider параметр сете вой библиотеки dbmssocn используется по умолчанию.

Х Внесите изменения в реестр клиента, чтобы задать TCP/IP как биб лиотеку по умолчанию. Дополнительную информацию о конфигури ровании сетевой библиотеки SQL Server см. в HOWTO: Change SQL Server Default Network Library Without Using Client Network Utility (Q250550) ( support/kb/articles/Q250/5/50.ASP).

46 Microsoft A.DO.NET Задание порта Если ваш экземпляр SQL Server настроен на прослушивание порта, отлич ного от порта 1433 по умолчанию, вы можете указать номер порта для под ключения следующими способами:

Х через Client Network Utility;

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

"Data Source=ServerName, PortNumber" Примечание ServerName может быть IP-адресом или DNS-именем. Для опти мальной производительности указывайте 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 ( get=/support/kb/articles/Q250/3/67.ASP).

Работа с большими двоичными объектами (BLOB) В настоящее время многим приложениям приходится иметь дело не толь ко с традиционными символьными и числовыми данными, но и с такими форматами данных, как графика и звук, и даже с еще более сложными ти пами данных вроде видео. Существует масса разных форматов графики, звука и видео. Однако, с точки зрения хранения, всю информацию таких форматов можно рассматривать как крупные массивы двоичных данных, обычно называемые большими двоичными объектами (Binary Large Ob jects, BLOB).

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

Руководство по архитектуре доступа к данным на платформе,NET Вообще говоря, для хранения двоичных данных с размером меньше 8 Кб лучше применять тип данных varbinary, а для хранения двоичных данных большего размера Ч тип 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.

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

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

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

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

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

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

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

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

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

public void StorePicture( string filename ) { // Считываем файл в байтовый массив FileStream fs = new FileStream( filename, FileMode.Open, FileAccess.Read );

byte[] imageData = new Byte[fs.Length];

fs.Read( imageOata, 0, (int)fs.Length );

fs.Closef);

SqlConnection conn = new SqlConnectionC'");

SqlCommand cmd = new SqlCommand("StorePicture", conn);

cmd.CoinmandType = CommandType.StoredProcedure;

cmd.Parameters.Add("@filename", filename );

cmd.Parameters["йfilename"].Direction = ParameterDirection.Input;

cmd.Parameters.AcJd("@blobdata", SqlDbType. Image);

cmd.Parameter$["@blobdata"]. Direction = ParafneterDirection.Input;

// Записываем байтовый массив е поле типа image cmd.Pararoeters["@blobdata"].Value = imageData;

try Руководство по архитектуре доступа к данным на платформе,NET conn.OpenO;

cmd.ExecuteNonQueryO;

catch throw;

finally conn.CloseO;

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

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

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

conn.0pen();

SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.SequentialAccess);

reader. ReadO;

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

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

// Выделяем память под массив байтов, предназначенный // для хранения данных поля byte[] imageData = new byte[bytesize];

long bytesread = 0;

tnt curpos = 0;

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

curpos += chunkSize;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Х Используйте транзакции, только когда вам нужны блокировки для вы полнения группы операций и введение в действие правил ACID.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Дополнительная информация Х При необходимости в хранимой процедуре можно управлять уровнем изоляции транзакции с помощью оператора SET TRANSACTION ISO LATION LEVEL. По умолчанию в SQL Server используется уровень изоляции Read Committed. Подробнее об уровнях изоляции транзак ций SQL Server см. SQL Server Books Online (раздел Accessing and Changing Relation Data, подраздел Isolation Levels).

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

54 Microsoft AD0.NET Применение автоматических транзакций Автоматические транзакции упрощают модель программирования, так как не требуют явным образом начинать новую транзакцию или фиксировать/ отменять транзакцию. Однако главное преимущество автоматических транзакций в том, что они работают совместно с DTC, что позволяет ох ватывать одной транзакцией несколько распределенных источников дан ных, В крупномасштабных распределенных приложениях это очень важно.

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

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

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

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

Х Чтобы не приходилось явно определять результат транзакции (фикса цию или откат), пометьте методы атрибутом AutoComplete. Если метод с таким атрибутом генерирует исключение, транзакция автоматически откатывается. Имейте в виду, что у вас сохраняется возможность явно указать результат транзакции (transaction outcome). Детали см. в раз деле Определение результатов транзакций, Дополнительная информация Х Дополнительную информацию об автоматических транзакциях СОМ+ см. в документации Platform SDK в разделе Automatic Transactions Through СОМ+.

Х Пример транзакционного.NET-класса см. в приложении Программи рование транзакционного класса.NET.

Руководство по архитектуре доступа к данным на платформе.NET Настройка уровней изоляции транзакций В СОМ+ 1.0, т. е. в версии, предоставляемой Windows 2000, используется уровень изоляции транзакций Serialized. Это обеспечивает высшую сте пень изоляции, но за счет производительности. Общая производитель ность системы снижается, так как задействованные в транзакции диспет черы ресурсов (обычно базы данных) должны поддерживать на время транзакции блокировки и по чтению, и по записи. В течение этого време ни остальные транзакции блокируются, что может значительно ухудшить масштабируемость приложения.

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

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

[Transaction(TransactionOption. Supported, Isolation=TransactionIsolationL.evel.ReadCommitted)] public>

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

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

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

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

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

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

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

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

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

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

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

[AutoComplete] void SomeMethodO { try { // Открываем соединение и выполняем операции над базой данных } catch (SqlException sqlex ) < LogException( sqlex );

// протоколируем параметры исключения throw;

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

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

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

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

void SomeOtherMetliodO { try { // Открываем соединение и выполняем операции над базой данных ContextUtil.SetComplete();

// еручнуя голосуем за фиксацию транзакции I catch (SqlException sqlex) { LogExceptionf sqlex );

// протоколируем параметры исключения ContextUtil.SetAbortO;

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

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

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

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

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

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

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

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

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

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

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

Х требований к производительности;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Со13 Остальные поля...

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

CREATE PROCEDURE RetrieveDataPageu @lastKey char(40), &pageSize int AS Руководство по архитектуре доступа к данным на платформе.NET SET RGWCOUNT @pageSize SELECT Coll, Col2. Col3. CoU, Col1+Col2+Col3 As KeyField FROM SampleTable WHERE [стандартное условие поиска] AND Со11+Со12+Со13 > @lastKey ORDER BY CoU ASC, Col2 ASC, Col3 ASC GO Для управления загрузкой таблицы порциями клиент должен сохранять последнее значение столбца KeyField, возвращаемого хранимой процеду рой, и при новом вызове передавать его этой процедуре.

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

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

Х Наследуйте свой класс от класса ServicedComponent в пространстве имен System.EnterpriseServices.

using System.EnterpriseServices;

public>

64 Microsoft ADO.NET [ConstructionEnabled(De"fault= default DSN")] public>

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

} Х Укажите для сборки строгое имя (strong name), подписав ее с помощью атрибута AssemblyKeyFile или Assembly Key Name. У любой сборки, за регистрированной в СОМ+ Services, должно быть строгое имя. Допол нительную информацию о строгих именах сборок см. по ссылке /msdn.microsoft.com/library/en-us/cpguidnf/html/cpconworkingwithst rongly-namedassemblies.asp.

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

// Атрибут ApplicationName содержит имя СОМ+-приложения, // в котором будут храниться компоненты сборки [assembly : ApplicationName("DataServices")] // Атрибут ApplicationActivation,ActivationOption определяет, // в какой процесс компоненты загружаются при активации:

// Library - компоненты выполняются в процессе создавшего их приложения, // Server - компоненты выполняются в системном процессе dllhost.exe [assembly: ApplicationActivation(ActivationOption.Library)] В следующем фрагменте кода показан обслуживаемый компонент DataAc cessComponent, использующий строки инициализации СОМ+ для получе ния строки подключения к базе данных.

using Systen;

using System.EnterpriseServices;

// Атрибут ApplicationName содержит имя СОМ+-приложения, // в котором будут храниться компоненты сборки Руководство по архитектуре доступа и данным на платформе.NET [assembly : ApplicationName("DataServices")] // Атрибут ApplicationActivation.ActivationOption определяет.

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

// Library - компоненты выполняются в процессе создавшего их приложения, // Server - компоненты выполняются в системном процессе dllhost.exe [assembly: ApplicationActivation(ActivationOption. Library)] // Подписываем сборку. Ключевой файл с расширением snk // создается утилитой sn.exe.

[assembly: AssemblyKeyFile("DataServices.snk")] [Const ructionEnabled(Default="Default DSN")] public>

public DataAccessComponent О { // Конструктор вызывается при создании экземпляра } public override void Construct( string constructString ) { // Метод Construct вызывается сразу после конструктора.

// Единственный аргумент - OSN, заданное при настройке.

this. connectionString = constructString;

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

using System. Data;

using System. Data. SqlClient;

public DataTable RetrieveRowsWithDataTableO { using ( SqlConnection conn = new SqlConnection(connectionString) ) < SqlCommand cmd = new SqlCommandC'DATRetrieveProducts", conn);

cmd.CommanoType = CommandType.StoredProcedure;

SqlDataAdapter da = new SqlDataAdapter( cmd );

DataTable dt = new DataTable("Products");

da.Fill(dt);

return dt;

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

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

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

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

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

Использование SqIData Reader для чтения нескольких записей Как это делается, показано в следующем фрагменте кода, using System.10;

using System.Data;

using System.Data.SqlClient;

public SqlDataReader HetrieveRowsWithDataReader() SqlConnection conn = new SqlConnection( "server=(local);

Integrated Security=SSPI;

database=northwind");

SqlCommand cmd = new SqlCommandC'DATRetrieveProducts", conn );

cmd,CommandType = CommandType.StoredProcedure;

try { conn.OpenO;

// Генерируем класс чтения. CommandBehavior.CloseConnection // означает, что соединение закрывается при закрытии объекта // класса чтения.

return{ cmd.ExecuteReader( CommandBehavior.CloseConnection ) );

} catch < conn.Closef);

throw;

) // Показываем список продуктов, используя консоль private void DisplayProductsO { SqlDataReader reader = RetrieveRowsWithDataReaderO;

while (reader.Reacf( Руководство по архитектуре доступа к данным на платформе.NET Console. WriteLine("{0} {1} {2}", reader.GetInt32(0).ToString(), reader. GetString(l) );

} reader. Close();

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

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

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

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

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

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

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

public void RetrieveAndDisplayRowsWithXmlReaderO { SqlConnection conn = new SqlConnection(connectionString);

SqlCommand cmd = new SqlCommand("DATRetrieveProductsXML", conn );

cmd.CommandType = CommandType.StoredProcedure;

try I conn.0pen();

XmlTextReader xreader = (XmlTextReader)cmd. ExecuteXmlReaderf);

while ( xreader. Read() ) { if ( xreader. Name == "PRODUCTS" ) I string strOutput = xreader. GetAttributeC'ProductlD");

68 Microsoft ADO.NET strOutput += " ";

strOutput += xreader.GetAttribute("ProductName");

Console. WriteLineC strOutput );

xreader.CloseC);

} catch { throw;

} finally { conn.Close();

} :

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

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

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

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

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

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

Руководство по архитектуре доступа к данным на платформе.NET void GetProductDetails( int ProductID, out string ProductName, out decimal UnitPrice ) i SqlConnection conn = new SqlConnection( "server=( local) integrated Security=SSPI;

database=Northwind");

// Создаем объект команды для вызова хранимой процедуры SqlCommand cmd = new SqlCommandC "DATGetProductDetailsSPOutput", conn );

cmd.CommandType = CommandType.StoredProcedure;

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

// @ProductID int INPUT // @ProductName nvarchar(40) OUTPUT // @UnitPrice money OUTPUT // Для выходных параметров нужно явно задавать направление SqlParameter paramProdlD = cmd. Parameters. Add( "йProductID", ProductID );

paramProdlD. Direction = ParameterDirection. Input;

SqlParameter paramProdName = cmd. Parameters. Add( "^ProductName", SqlDbType. VarCtiar, 40 ) ;

paramProdName. Direction = ParameterDirection. Output;

SqlParameter paramUnitPrice = cmd. Parameters. Add( "@UnitPrice", SqlDbType. Money );

paramUnitPrice. Direct ion = ParameterDirection. Output;

try < conn.OpenQ;

// Для выполнения команды используйте ExecuteNonQuery.

// Хотя этот метод не возвращает записей, происходит заполнение // выходных параметров (и возможно, возвращаемых значений).

cmd. ExecuteNonQuery( ) ;

// Возвращаем выходные параметры хранимой процедуры ProductName = paramProdName. Value. ToString();

UnitPrice = (decimal)paramUnitPrice. Value;

} catch ( throw;

} finally { conn.CloseO;

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

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

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

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

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

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

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

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

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

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

void GetProductDetailsUslngReader( int ProductID, out string ProductName, out decimal UnitPrice ) { SqlConnection conn = new SqlConnection( "server=(local>;

Integrated Security=SSPI;

database=Northwind");

// Создаем обьект команды для выполнения хранимой процедуры SqlCommand cmd = new SqlCommand{ "DATGetProductDetailsReader", conn );

cmd.CommandType = CommandType.StoredProcedure;

// Указываем параметры хранимой процедуры // йProductID int INPUT SqlParameter paraiflProdlD = cmd.Parameters.Add( "@ProductID", ProductID );

paramProdlD.Direction = ParameterDirection.Input;

Pages:     | 1 | 2 | 3 | 4 | 5 |   ...   | 6 |    Книги, научные публикации