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

David Sceppa Microsoft Press Дэвид Москва 2003 Р Г Г If Р F г V А Л А - л г УДК 004.45 32.973.26-018.2 С28 Д. ...

-- [ Страница 7 ] --

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

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

Завершив работу с мастером, вы увидите в панели компонентов формы объекты QleDbDataAdapter и Измените их имена соответственно на и Щелкните в панели компонентов правой кнопкой и выберите в контекстном меню команду Generate DataSet. В открывшемся диалоговом окне измените имя нового класса DataSet на и щелкните ОК. В окне Solution Explorer появится новый элемент Ч а в панели компонентов Ч экземпляр класса DataSet. Задайте этому экземпляру имя Столбец OrderlD таблицы Orders Ч это столбец с автоинкрементом. Как вы помните из главы 6, в объекте DataSet свойствам Step столбцов с автоинкрементом рекомендуется задавать значение Давайте зададим указанные свойства столбца OrderlD в только что созданном нами се DataSet со строгим контролем типов. В окне Solution Explorer дважды щелкни ГЛАВА 13 Создание эффективных Windows-приложений те файл схемы класса со строгим контролем типов столбец значение -1. Закройте окно и сохраните изменения.

Этап 2. Добавление связанных с данными элементов управления TextBox На форме уже есть объекты и DataSet. Теперь давайте добавим эле менты управления при помощи которых на форме будет отображаться информация о конкретном заказе. Чтобы сделать пользовательский интерфейс интуитивно-понятным, мы также добавим для каждого элемента управления мет ку с описанием отображаемых элементом данных, добавим метку и элемент управления TextBox для столбца OrderlD.

Перетащите с панели инструментов на форму элемент управления Label. Задайте его свойству Name значение а свойству Text Ч значение Order За тем перетащите с панели инструментов на форму элемент управления TextBox.

Задайте его свойству Name значение а свойству Text Ч пустое значение.

Сейчас мы по-прежнему имеем дело с простым элементом управления TextBox.

Чтобы связать его со столбцом OrderlD объекта DataSet, перейдите в окно ies.

Найдите раздел Если свойства упорядочены по категориям (кон фигурация по умолчанию), данный раздел отображается в категории Data. Я пред почитаю упорядочивать свойства по алфавиту;

при этом раздел (DataBindings) расположен в верхней части списка свойств. Нам нужно связать содержимое стол бца OrderlD со свойством Text элемента управления. Раскройте раздел (Data Bindings), выберите элемент Text и щелкните направленную вниз стрелку, просмотреть список столбцов. В списке указан объект DataSet. Раскрыв его узел, вы увидите список объектов В нашем случае список содержит только один объект DataTable Ч Orders. Раскрыв узел этого объекта, вы увидите список объектов Щелкните столбец OrderlD (рис. 13-2).

Рис. 13-2. Связывание свойства Text элемента управления TextBox с одним из объектов DataColumn из состава DataSet Следуя инструкциям, приведенным ранее, создайте элементы управления Label и TextBox для столбцов EmployeelD и Используйте те же префиксы имен и тот же формат свойства Text элементов управления. Свяжите 492 Часть IV Создание эффективных приложений с использованием элементы управления с столбцами объекта Расположите элементы управления, как показано на рис.

X Т! J ( fflffl Рис. 13-3. Добавление на форму элементов управления Label и TextBox Как связать элемент управления TextBox с объектом в период разработки очень легко. Кроме того, такое связывание можно осуществить про граммно:

Visual Basic dsChapter13, Visual C# Этот код связывает свойство Text элемента управления со столбцом OrderlD таблицы Orders из состава объекта DataSet Примечание В приложении я задал свойству Readonly элемента управления связанного со столбцом OrderlD, значение чтобы исклю чить редактирование содержимого этого столбца пользователями. По умолчанию цвет текста, доступного только для чтения, в элементе уп равления TextBox Ч серый, а не черный. Лично мне это не нравится, и я задал ForeColor данного элемента управления значение Black.

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

ГЛАВА 13 Создание эффективных Windows-приложений Этап 3. Получение данных Теперь у вас есть объект и несколько элементов управления TextBox, свя занных со столбцами этого объекта, однако нет никаких данных для вывода на экран. Запустив проект, вы увидите форму с пустыми элементами управления и TextBox. Это совершенно верно Ч ведь в объекте DataSet пока нет записей о заказах.

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

Вам потребуется добавить код, чтобы приложение при запуске выбирало из БД записи о заказах. Дважды щелкните Откроется окно кода, и вы увидите процедуру обработки события Load. Перед ней добавьте такую строчку кода:

Basic Dim As String = Visual C# string strCustomerlD = "ALFKI";

Затем добавьте в процедуру обработки события Load такую строчку:

Visual Basic = strCustomerlD Visual C# = Первая строка необходима потому, что созданный нами для объекта Ч параметризованный. Добавив этот код и запустив проект, вы уви дите на форме сведения о заказе.

Кроме в правый нижний угол формы приложения я добавил кнопку Close.

В ее событии я вызываю метод Close формы, чтобы завершить работу при ложения. Чтобы сделать то же самое в своем приложении, перетащите кнопку с панели инструментов, задайте ее Name значение и свойству Text Ч значение Close. Дважды щелкните чтобы просмотреть код обработки ее события Click. Дополните его следующими строками:

Visual Basic Visual 494 Часть IV Создание приложений с использованием Этап 4. Добавление кнопок для перемещения по содержимому объекта DataSet На текущий момент приложение лишь отображает сведения о заказе. Можно до бавить код, который проверяет свойства Count объекта Orders из состава нашего объекта DataSet и сообщает, сколько возвращено записей. При не очень-то если способно отображать лишь один заказ. Давайте реализуем функциональность, обеспечивающую просмотр числа возвращенных записей и перемещение между ними.

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

Рис. 13-4. Добавление на форму элементов управления для перемещения по содержимому объекта DataSet управления предоставляют данную функциональность при помощи экземпляра класса CurrencyManager. И хотя из имени класса следует, что он вы ступает в роли финансового консультанта, на самом деле именно этот класс обес печивает функционирование связывания с данными. Windows-форма предостав ляет свойство обращаться к объектам CurrencyManager, которые контролируют связанные с данными управления формы.

Элементы управления TextBox связаны с одной записью объекта DataTable Orders. Чтобы сменить в этих элемента управления запись, изме ните значение свойства Position экземпляра класса CurrencyManager. Для перехо да к следующей записи увеличьте значение свойства Position на единицу, а для перехода к предыдущей записи Ч уменьшите его аналогичным образом.

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

ГЛАВА 13 Создание эффективных Windows-приложений Visual уровня формы Dim As Private Sub frmEditOrders_Load...

= "Orders"), AddHandler AddressOf End Sub Private Sub = "Order & + 1 & _ of & End Sub Private Sub sender As Object, e As End Sub Private Sub sender As Object, ByVal e As End Sub Private Sub = End Sub Private Sub -= End Sub Private Sub += End Sub Private Sub = - End Sub Visual C# уровня формы CurrencyManager cmOrders;

private void 496 Часть IV Создание эффективных приложений с использованием i cmOrders = += new += new !

private void !

= "Order + + 1} + of + private void sender, e) i private void sender, EventArgs e) \ private void sender, e) { = 0;

} private void sender, e) { } private void sender, e) < private void sender, e) { = - 1;

;

Этап 5. Добавление кнопок Add и Delete Теперь пользователь получил возможность все записи о заказах, возвращенные объектом а также изменять состав заказа, редактируя содержимое связанных элементов управления TextBox.

ГЛАВА Создание эффективных Windows-приложений Запустите форму и измените значение поля в первом заказе. Вы помните оригинальное и новое значение данного поля?

поля EmployeelD в первом перейдите к следующему и затем снова верни тесь к первому заказу. Вы увидите, что новое значение поля EmployeelD по-пре жнему на месте. Я изо всех борюсь с желанием чудесным водевильным голосом спросить: Это введенное вами значение поля EmployeelD? И хотя форма позволяет редактировать содержимое заказа, добавлять и уда лять заказы нельзя... пока нельзя. Объект Man age предоставляет методы, позволяющие добавлять и элементы из структуры данных, с которой свя заны элементы управления. Метод добавляет новый элемент, а метод Ч удаляет существующий элемент. Элементы управления, связанные с объектом соответствующим образом реагируют на вызов любого из этих методов. Если вы добавили элемент с помощью метода элементы управления отобразят содержимое новой записи. При вызове метода элементы управления отобразят содержимое следующей доступной записи.

Visual Basic Private Sub End Sub Private Sub > 0 Then Else Order to "Delete Order", End If End Sub Visual C# private void sender, e) < private e) Хi if > 0) else Order to "Delete Order", 498 Часть IV Создание эффективных приложений с Этап 6. Передача изменений в БД Теперь наше приложение позволяет редактировать, добавлять и удалять заказы. Но как вы, возможно, заметили, приложение не передает эти изменения в БД. Наде юсь, вы достаточно хорошо помните материал глав 5 и 10, чтобы понимать, по чему это так. Связанные элементы управления изменяют содержимое однако у нас нет кода, вызывающего метод Update.

Мы создали объект DataAdapter с помощью мастера Data Adapter Configuration Wizard, и поэтому определять логику обновления не требуется. За нас ее создал мастер. Все, что надо сделать нам. Ч добавить на форму кнопку Submit Changes и вызвать в процедуре обработки события Click этой кнопки метод чтобы передать изменения в БД. Код перехватывает возвращаемое значение мето да Update объекта DataAdapter, указывающее число переданных в БД изменений.

Я добавил пару строк кода, которые выводят число измененных записей о за казах, при ошибке обновления перехватывают генерируемое исключение и вы водят диалоговое окно, если объект не содержит отложенных изменений, Суть этого кода Ч все тот же метода Visual Basic If Then Try Dim intOrdersModified As Integer intOrdersModified = Dim As String = & intOrdersModified " "Update Catch ex As Exception "Update End Try Else changes to If Visual C# if i { int intOrdersModified;

intOrdersModified = Orders);

string strOutput;

strOutput = "Modified + intOrdersModified + "Update ГЛАВА 13 Создание эффективных Windows-приложений catch (Exception ex) "Update else changes to 7. Добавление кнопок Edit, Update и Cancel Созданное нами приложение Ч довольно простое: на форме лишь несколько эле ментов управления. Тем не менее работа с приложением не настолько интуитив но понятна, как может показаться.

Запустите приложение и измените значение поля первого заказа.

Щелкните кнопку Submit Changes. Откроется диалоговое окно с сообщением об отсутствии изменений для передачи в БД. Что же произошло?

Согласно нашему коду, объект не содержит каких-либо изменений. Так что же случилось с только что внесенными вами изменением? Объект Manager по-прежнему хранит его и пока не записал в объект Если перей ти к следующему заказу и щелкнуть кнопку Submit Changes, изменение будет ус пешно передано в БД.

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

На этапе 7 создания приложения я добавил кнопки Edit, Cancei и Update. При запуске приложения все данные в элементах управления TextBox доступны толь ко для чтения. Чтобы изменить содержимое следует щелкнуть кнопку Edit.

Лишь после этого удастся редактировать содержимое элементов управления TextBox (кроме элемента, связанного со столбцом Кнопки перемещения, а также кнопки Add, Edit, Delete и Submit Changes на период редактирования от ключаются. Для продолжения работы следует щелкнуть кнопку Update или Cancel (рис. 13-5). При щелчке кнопки Update изменения текущего заказа ются. а при щелчке кнопки Cancel Ч отменяются.

На добавление такой требуется лишь несколько минут, и они тратятся с умом. В результате работа приложения станет более интуитивно понятной. Исчезнут проблемы с передачей изменений, связанные с что из менения не подтверждены. Кроме того, вам не придется напрягаться, чтобы по как и когда объект CurrencyManager подтверждает изменения содержимого DataSet.

500 Часть эффективных приложений с использованием tD Dale.

Update Рис. 13-5. Принудительное подтверждение или отмена изменений в составе текущего заказа Я добавил в код формы процедуру которая принимает ло гическое значение, указывающее, может ли пользователь редактировать текущий заказ. Процедура в соответствии с принятым значением задает значения Readonly элементов управления TextBox, а также значения свойств кно пок. Процедура вызывается в событии кнопок Add, Update и Cancel.

Visual Basic Private Sub blnEdit As = Not blnEdit = Not blnEdit = Not blnEdit = Not blnEdit = Not blnEdit = Not blnEdit = Not blnEdit = blnEdit = blnEdit = Not blnEdit = Not blnEdit = Not blnEdit = Not blnEdit End Visual C# private void SetOrdersEditMode(bool blnEdit) { = = = = ГЛАВА 13 Создание эффективных Windows-приложений = = = = = blnEdit;

= IblnEdit;

= = = Этап 8. Просмотр дочерних данных Теперь наше приложение просматривать и изменять данные таблицы Orders. Тем не менее такая функциональность не очень-то полезна, если не пре доставить возможность изменять состав На рис. 13-6 показан пользовательский интерфейс следующей версии нашего приложения. Как видно, я добавил сетку, данные из таблицы Order Details. Когда вы переходите от одного заказа к другому, в сетке отображаются только связанные с этим заказом записи.

Рис, 13-6. Просмотр заказа и связанных с ним товаров Чтобы добавить данную функциональность в приложение:

добавьте объект выбирающий все записи таблицы Order соответствующие заказам клиента;

2. воссоздайте объект со строгим контролем чтобы в будущем добавить в него объект с новыми полученными на первом этапе. После этого добавьте в объект DataSet объект чтобы упро стить поиск связанных с заказом товаров;

3. добавьте на форму элемент управления и свяжите с объектом DataSet таким образом, чтобы в нем отображались лишь товары из состава текущего заказа;

4. добавьте в процедуру, обрабатывающую событие кнопки Submit Changes, логику для передачи в БД изменений из обоих объектов 502 Часть Создание эффективных приложений с использованием Получение только списка заказанных клиентом товаров Создать объект получающий только заказы конкретного клиента, весьма просто, поскольку в таблице Orders определен столбец Б таб лице Order Details такого столбца нет. Таким запрос, получающий толь ко список заказанных клиентом должен одновременно ссылаться на таб лицы Order Details и Orders.

Есть несколько способов структурировать такой запрос. Бот три из них:

SELECT Orders 0 JOIN [Order Details] D ON = D.OrderlD WHERE ?

SELECT Quantity FROM [Order Details] WHERE OrderlD IN (SELECT OrderlD FROM Orders WHERE CustomerlD = ?) SELECT D.OrderlD, D.UnitPrice, [Order D, Orders WHERE D.OrderlD = AND ?

Согласно SQL Query Analyzer, выводящему примерный план выполнения, SQL Server создает для каждого из этих запросов одинаковый план, По двум причи нам я отдаю предпочтение синтаксису третьего запроса. Во-первых, он самый интуитивно понятный. Во-вторых, мастер Data Adapter Configuration Wizard не способен обработать такой синтаксис из-за наличия параметра в подзапросе.

что мастер использует синтаксис INNER JOIN, И хотя при создании приложения я использовал синтаксис третьего из приведенных выше запросов, преобразовал мой SQL-оператор согласно синтаксису первого запроса. Каждому Ч свое.

Создав с помощью этого запроса новый объект назовите его Добавление объекта DataTable Order Details в класс DataSet со строгим контролем типов Добавив объект для записей из Order мож но воспользоваться диалоговым окном Generate Dataset и добавить в класс DataSet со строгим контролем типов новый объект DataTable. Подробнее Ч на рис.

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

которая соответствует новому объекту DataAdapter.

Щелкните ОК, и Visual Studio добавит новый объект DataTable в имеющийся класс DataSet со строгим контролем типов. Если оставить помеченными обе таб лицы, Visual Studio перезапишет объект DataTable Orders, имеющийся в объекте DataSet со строгим контролем типов. Это означает, что значения свойств пропадут. Сбрасывать эти свойства нетрудно, но уто Теперь, когда класс DataSet со строгим контролем типов содержит объекты DataTable, соответствующие таблицам Orders и Order Details, можно добавить объект ГЛАВА Создание эффективных Windows-приложений определяющий между этими двумя объектами DataTable на основе столбца В окне Solution Explorer дважды щелкните.xsd-файл класса, чтобы запустить конструктор XML Designer. Перетащите столбец из объекта DataTable Orders в объект DataTable Order Прими те предлагаемые конструктором значения параметров по умолчанию.

a that 3.

which tablets) add Orders fidd to the designer.

Рис. 13-7. Добавление объекта DataTable Order Details в класс DataSet со строгим контролем типов Приложение также добавляет вычисляемое поле, отображающее общую сто имость заказанных единиц конкретного товара, Вы можете сделать то же добавив в объект DataTable Order Details новый столбец. Задайте тип данных это го столбца Decimal задайте значение Закройте конструктор и сохраните изменения.

Добавление элемента управления DataGrid, отображающего дочерние данные Связать элемент управления DataGrid с данными просто. Достаточно задать значения свойств Ч и Добавьте на форму элемент управления DataGrid и задайте ему имя В окне Properties выберите свойство DataSource. список справа, что бы просмотреть перечень доступных источников данных. Вы увидите объект DataSet и отдельные объекты DataTable. Выберите свойство DataMember. Для него также имеется список возможных значений (рис. 13-8). Раскрыв в списке узел объекта DataTable Orders, вы увидите созданный вами ранее объект DataRelation.

Если задать свойству DataMember значение DataRelation, элемент управления Data Grid отобразит только дочерние записи, используя объект DataRelation.

Чтобы связать элемент управления DataGrid с данными в период воспользуйтесь таким кодом:

504 Часть IV Создание эффективных приложений с использованием Visual = = Visual = DataHember = em. Windows, (none) Details - Orders :

Control Microsoft Control Рис. 13-8. Задание значения элемента управления дочерние записи В приложении, в конце обрабатывающей событие Load формы, есть строка вызывающая процедуру Эта процедура добавляет в элемент управления DataGrid новый объект позволяющий управлять внешним видом элемента управления: выбирать отображаемые столбцы, а также задавать для каждого из них формат и параметры выравнивания.

Передача в БД изменений из обеих таблиц Глава посвящена проблемам передачи в БД иерархических изменений. По су ществу, новые записи требуется передавать, начиная с верхней части иерархии (сначала записи о заказах, потом записи о составе этих заказов), а удаленные за писи Ч начиная с нижней ее части (сначала записи о составе заказов, затем за писи о заказах). Таким образом, нельзя взять и целиком передать вызываемому методу объект Для начала следует передать в БД информацию о новых и измененных зака зах. После этого Ч все изменения таблицы Order Details. Затем Ч удаленные за казы. Изучив код процедуры, обрабатывающей событие Click кнопки Submit Changes, вы что он использует же Я этот код ниже, опус тив для удобочитаемости блок ГЛАВА 13 Создание эффективных Windows-приложений Visual Basic Dim As Integer Dim As Dim dvrs As новые или измененные заказы = Or aRowsToUpdate = = все DataTable, таблице Order Details intDetailsModified = удаленные заказы dvrs = aRowsToUpdate = dvrs) intOrdersModified += Dim As String = "Modified & intOrdersModified & & & _ "Modified & & "Update Visual C# int intDetailsModified;

aRowsToUpdate;

DataViewRowState dvrs;

новые или измененные dvrs = ] aRowsToUpdate = dvrs);

intOrdersModified = все изменения DataTable, таблице Order Details intDetailsModified = удаленные заказы dvrs = = intOrdersModified += string strOutput;

strOutput + intOrdersModified + order(s)\n\r" + + + "Update Этап 9. Связывание второй формы с тем же источником данных Связать несколько элементов управления на разных формах с одним источником данных сложно, но можно. Прежде чем подробно рассматривать процесс такого связывания, я отвлекусь и как усовершенствовать интерфейс приложе ния, чтобы сделать редактирование данных еще более простым, Часть IV Создание эффективных приложений с Элемент управления DataGrid Ч полезное и мощное средство. Я часто исполь зую такие элементы управления для вывода содержимого нескольких записей. Тем не менее мне не очень-то ситуация, когда пользователю предоставлена возможность редактировать данные непосредственно в DataGrid. Вы, вероятно, знаете, что изменения в элементе управления DataGrid удается отменить, нажав Escape или однако редко кто из начинающих пользователей понимает, что это вообще возможно.

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

Рис. 13-9. Редактирование информации о заказанном товаре на новой форме Здесь можно редактировать запись о товаре и точно так же, как при измене нии записи о заказе, для прекращения редактирования следует кнопку Update или Cancel.

Теперь вернемся к связыванию нескольких элементов управления, расположен ных на разных формах, с одним источником данных. Элементы управления Box новой формы связаны с той же записью данных, которая отображается в элемен те управления DataGrid основной формы. И хотя связать таким образом элемен ты управления в период разработки нельзя, это допустимо в период выполнения, как показано ниже.

Просмотрев код, выполняющийся по щелчку кнопки Edit, расположенной под элементом управления DataGrid, вы увидите, что он создает экземпляр формы с информацией о заказанных товарах и затем вызывает метод этой фор мы. Данный метод принимает в качестве параметра объект Код метода показан далее. Как видно, он использует объект Currency чтобы связать элементы управления TextBox на новой форме с той же записью данных. Свойство Current объекта возвращает объект управления TextBox можно связать с объектом DataView, чтобы код на основе свойства объекта обращался к объек ту DataView. с которым связан возвращенный объект DataRowView, ГЛАВА Создание эффективных Visual Basic Dim As Dim vueDetail As DataView Public Sub EditDetail(ByVal cm As drvDetail = DataRowView) vueDetail = = vueDetail, vueDetail, vueDetail, "Quantity") vueDetail, If = Then Else End If End Sub Visual C# DataRowView drvDetail;

DataView vueDetail;

public void EditDetail(CurrencyManager cm) { drvDetail = (DataRowView) vueDetail = = vueDetail, vueDetail, vueDetail, vueDetail, if == cm.

else cm.

Часть IV Создание эффективных приложений с использованием Этап 10. Совершенствование пользовательского интерфейса Наше приложение позволяет просматривать и редактировать о за казах клиента, однако стоит сделать кое-что еще, чтобы работать с ним стало еще удобнее.

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

Кроме того, не лучшим образом выглядит формат стоимости единицы и стоимо сти общего числа заказанных единиц товара.

Посмотрим, как представить данные в более понятном формате (рис.

Рис. 13-Ю. Отображение данных в понятном формате Добавление функции поиска с использованием элемента управления На рис. я заменил элемент управления TextBox, отображавший значение поля текущей записи о заказе, элементом управления ComboBox, отобра жающим имя принявшего заказ сотрудника. Добавить такую функциональность на самом деле очень просто Ч достаточно задать значения четырех свойств эле мента ComboBox.

Прежде чем добавить на форму элемент управления ComboBox и проверить предлагаемую функциональность в действии, следует добавить возвращающий информацию из таблицы Employees. Я создал этот объект посред ством следующего SELECT EmployeelD, + AS FROM Employees Добавив объект для выборки информации о сотрудниках, нужно создать экземпляр объекта со строгим контролем типов и добавить в него объект Employees.

Нам нужно, чтобы элемент управления ComboBox отображал значения из стол бца объекта DataTable Employees. Когда пользователь выбирает ГЛАВА 13 Создание эффективных Windows-приложений имя сотрудника, Box должен принять значение из столбца и поместить его в поле EmployeelD текущей записи о заказе. На самом деле мы свяжем элемент управления одновременно с двумя разными источни ками данных Ч с объектом Employees и с текущей записью о заказе.

Примечание Приложение не будет изменять содержимое таблицы и поэтому создавать логику обновления для объекта взаи модействующего с таблицей Employees, не требуется. Чтобы сообщить об отсутствии такой необходимости мастеру, достаточно в окне SQL Statement переключатель Advanced Сначала давайте посмотрим, как связать элемент управления ComboBox с объек том DataTable Employees. У ComboBox есть свойство значение кото рого задается так же, как и значение свойства элемента ния DataGrid. Выберите из списка доступных источников данных объект Свойство DisplayMember определяет, значениями какого столбца ComboBox запол няет список, и принимает соответствующего столбца. В нашем случае это имя столбца EmployeeName объекта DataTable Employees. Затем задайте свойству Value Member имя столбца объекта Employees.

Все, что осталось сделать, Ч сопоставить элемент управления ComboBox с полем EmployeelD текущей записи о заказе. Это осуществляется аналогично связыванию свойства Text элемента управления TextBox со столбцом объекта DataSet. Найди те в окне Properties раздел (DataBindings), раскройте его. найдите свойство Selected Value и свяжите это свойство со столбцом EmployeelD объекта DataTable Orders, В период выполнения осуществить все это следующий код:

Visual Basic = = = Visual C# = dsChapter13;

= cboEmployee. DataBindings.

Теперь при перемещении по записям о заказах будет отображаться значение поля а не поля EmployeelD. Кроме того, можно отредактировать запись о заказе и изменить значение поля EmployeelD, изменив имя отображаемое в элементе управления ComboBox, Управление форматом связанных данных Тип данных столбца объекта DataSet Ч Decimal. Как элемент управления TextBox на форме с информацией о заказанных товарах, связанный 510 Часть IV Создание эффективных с использованием со столбцом UnitPrice, отображает содержимое этого столбца с использованием стандартного числового формата. Стоимость единицы товара, равная будет отображаться в TextBox как Можно написать код, чтобы вручную изменить формат отображения данных на более подходящий.

В одном из предыдущих разделов главы я приводил фрагмент кода, связываю щий свойство Text элемента управления TextBox со столбцом объекта Visual Basic Visual C# Метод Add возвращает объект который реагирует на события объекта и перемещает данные между элементом управления TextBox и столбцом, с которым этот элемент связан, Объект Binding предоставляет два события Ч Format и Parse. Событие Format наступает, когда объект Binding загружает из источника в свойство, с ко торым связан. Событие Parse наступает, когда объект Binding считывает данные из связанного свойства и передает их в источник. С помощью этих событий удается управлять форматом отображения данных в связанных элементах управ ления TextBox.

Следующий фрагмент кода приложения выводит значение поля UnitPrice в элементе управления TextBox с использованием соответствующего типу данных currency. Изменение формата представления данных осуществляется с помощью метода метод класса Decimal Visual Basic Public Sub cm As CurrencyManager) Dim b As Binding b = AddHandler AddressOf End Private Sub sender As Object, cevent As If Not Is GetType(String) Then Exit Sub End If If Is Then = Else = ГЛАВА 13 Создание эффективных Windows-приложений End If End Sub Private Sub sender As Object, As ConvertEventArgs) If Not Is GetType(Decimal) Then Exit End If = Nothing) End Sub Visual C# public void cm) Binding b;

b = += new += :

private void sender, ConvertEventArgs cevent) { if if == = else = I private void sender, ConvertEventArgs cevent) ;

if return;

= Decimal. Parse(cevent. Value.

null);

512 Часть IV Создание эффективных приложений с использованием Этап Если хочешь сделать что-то хорошо...

Давайте ненадолго прервемся и окинем взглядом созданное нами приложение.

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

При первоначальном связывании элементов управления у нас были весьма ограниченные возможности для контроля их взаимодействия с данными объекта На этапе 10 мы добавили изменивший формат представления дан ных в элементах управления TextBox. В папке прилагаемого компакт-диска, соот ветствующей этапу 10 разработки приложения, также записан код, позволяющий форматировать и принимать значения Null. Можно дополнительный расширяющий возможности контроля связанных элементов но по мните: чем больше кода вы тем меньше от первоочередной став ки на связывание с данными.

Приведу пример. Я закончил этап и начал работать с приложением- приме ром, попутно обдумывая, как бы его усовершенствовать. Выяснилось, что на фор ме Edit Detail при изменении содержимого элементов управления TextBox, соот ветствующих столбцам Quantity и не обновлялось содержимое элемента управления TextBox ItemTotal. В связи с этим я решил поискать способ, который позволил бы автоматически обновлять содержимое элемента управления TextBox ItemTotal при изменении содержимого элементов управления TextBox, соответ ствующих столбцам Quantity и Price. Я пробовал задавать значение свойства Text элемента управления TextBox ItemTotal в событии Leave элементов управления TextBox, соответствующих столбцам Quantity и Price. Пытался вызывать метод Старался задействовать методы и Чего я только не делал, но так и не смог найти нужной функциональности. Полагаю, эту задачу удастся решить при помо щи связывания с данными, но все же у связывания с данными другое предназна чение.

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

Если для вывода данных в элементах управления и передачи изменений в струк туру данных вы используете собственный код, вы полностью управляете и отве чаете за взаимодействие пользовательского интерфейса и данных. На эта пе данные в элементах управления TextBox выводятся уже без помощи связы вания с данными. Просмотрев код формы Edit Orders, вы увидите а также код, определяющий, какой заказ и названия каких товаров отображать с использованием объектов Кроме того, формы включают код для проверки данных, вводимых в различные элементы управления TextBox.

ГЛАВА 13 Windows-приложений Если пользователь указал недопустимое значение, например вместо товара, система выдаст сообщение соответствующего содержания.

Резюме: связывание с данными Как показывают различные этапы разработки приложения, средства с данными, предоставляемые пакетом Windows Forms, позволяют создать мощный и надежный пользовательский интерфейс при минимуме кода. Тем не менее, пы таясь расширить возможности управления этим интерфейсом и добавляя в при ложение собственный код, вы в некоторых случаях обнаруживаете, что не допол няете средства связывания с данными, а сражаетесь с ними. Б этом случае лучше написать собственный код, реализующий взаимодействие данных и пользователь ского интерфейса.

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

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

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

Один из факторов, определяющий, какие данные выбирать, Ч среда приложе ния. В некоторых случаях пользователю требуется загрузить данные на ноутбук по модему на 28 кбит/с, поработать с ними в автономном режиме, изменить их и затем по все тому же модему подключиться к БД и передать отложенные измене ния. Обсуждаемый канал имеет весьма низкую пропускную которую нет смысла расходовать попусту, однако в соответствии с требованиями среды приложения вам все равно придется загрузить на ноутбук из БД все необходимые данные.

Стратегии обновления Приложение-пример кэширует изменения и передает обновления с использова нием оптимистического управления блокировками. Сейчас я расскажу о других стратегиях обновления 514 Часть IV Создание эффективных приложений с использованием Мгновенные и обновления Решение о том, следует ли передавать изменения в БД мгновенно или их и передавать их позже, зависит от потребностей вашего приложения.

Когда пользователь изменяет запись о заказе, приложение-пример не переда ет это изменение в БД немедленно. Оно средствами ADO.NET это об новление и передает его лишь по щелчку кнопки Submit Change.

Приложение легко модифицировать, чтобы изменение записи о заказе пере давалось в БД по щелчку кнопки Update. Когда пользователь щелкнет кнопку Edit, приложение позволит изменить о заказе и его составе. Если пользо ватель щелкнет кнопку Cancel, приложение отбросит сделанные изменения. Пос ле щелчка кнопки Update приложение, наоборот, сохранит изменения и затем с помощью объекта DataAdapter передаст их в БД.

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

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

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

Однако в отличие от пакета молока, у объекта DataSet нет срока хранения. Когда соответствующие записи БД изменяются, не наступает каких-либо событий объекта DataSet. Вполне что, когда пользователь вашего приложения изменит данные и попытается передать их в БД. они уже будут изменены в БД другим пользо вателем, поэтому попытка обновления не удастся.

Рассмотрим наше приложение-пример. При запуске оно выбирает данные.

Пользователь может щелкнуть кнопку Edit через какие-то секунды после запуска приложения. Чем дольше приложение работает, чем больше данные, Фактически перед изменением содержимого записи пройти минуты и даже часы.

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

Для повторной выборки содержимого записи создайте объект DataAdapter, выполняющий параметризованный запрос следующего вида:

SELECT,. FROM Таблица WHERE = ?

ГЛАВА 13 Создание эффективных Windows-приложений Если значение свойства объекта определено, объект Data Adapter обновит содержимое объекта используя данные из БД. Помни те: этот запрос не сгенерирует исключение, если другой пользователь удалил в БД соответствующую запись. В этом случае он просто не вернет записей. Метод возвращает целое число, соответствующее количеству выбранных из БД записей. Если метод Fill вернул 0, записи в БД больше нет. Можно обрабо тать эту ситуацию и изящно известить пользователя об отсутствии нужной записи.

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

Внимание! Пессимистическое блокировками Ч мощное и опасное средство. Бойтесь его. Сильно бойтесь.

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

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

Если при обновлении данных пессимистическое управление перед редактированием содержимого на запись БД следует нало жить Это гарантирует, что при обновлении не сбои из-за того, что запись успел изменить другой пользователь. Объектная модель ADO.NET предназначена для передачи обновлений с применением оптимистического уп равления блокировками. Как вы помните из глав ] 0 и объект по зволяет передавать в БД отложенные изменения из объекта Когда вы из меняете содержимое объекта не налагает блокировку на соот ветствующую запись БД. В ADO.NET, по крайней мере в первой версии этой мо дели, у объектов нет свойств, позволяющих организовать пессимистическое уп равление блокировкой.

И все же задействовать такой тип управления блокировкой можно, воспользо вавшись объектами Transaction. Тем не менее в транзакциях обычно выполняют нечто более сложное, чем простой запрос SELECT. Налагаются ли блокировки на данные при выполнении запроса SELECT в транзакции, зависит от БД, типа запроса и уровня изоляции транзакции.

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

изоляции транзакций в SQL Server по умолчанию Ч Read Committed (чтение при подтвержденной транзакции). При этом на запись, измененную в транзакции, сразу же налагается блокировка. При простой выборке содержимого записи с помощью 516 Часть IV Создание эффективных приложений с использованием запроса SELECT блокировка не налагается. Но если используется уровень изоля ции Read (повторяемое чтение) или Scrializable (серийный), то блоки ровка налагается и после выборки содержимого записи средствами запроса SELECT.

Некоторые БД позволяют использовать в запросах указания по блокировке. Б SQL Server запрос налагает на запись блокировку независимо от уровня изоляции транзакции:

SELECT ContactName, Phone Customers WITH WHERE = Подробнее о поддерживаемых уровнях изоляции транзакций и указаниях по блокировке Ч в документации вашей СУБД.

Следующий фрагмент кода при помощи объекта и указаний по блокировке, включенных в запрос SELECT, пессимистично налагает блокиров ку на БД Когда код вернет на сервере на запись налагается блокировка. Можно определить точку останова после вызова метода и убедиться в наличии блокировки. На этом этапе вы сможете просмотреть содержимое записи при помощи средства выполнения произволь ных запросов, например утилиты SQL Server Query Analyzer, но изменить это со держимое не удастся.

Visual Dim As String strConn = & _ "Initial strSQL = "SELECT CompanyName FROM Customers & "WITH (UPDLOCK) WHERE CustomerlO = Dim As New Dim txn As OleDbTransaction = Dim cmd As New txn) Dim da As New OleDbDataAdapter(cmd) Dim cb As New OleDbCommandBuilder(da) As New Dim As = = Visual C# string strConn, strSQL;

strConn = + "Initial StrSQL = "SELECT CustomerlD, CompanyName FROM Customers + "WITH (UPDLOCK) WHERE CustomerlD OleDbConnection = new QleDbConnection(strConn);

ГЛАВА 13 Создание эффективных Windows-приложений txn = = new txn);

da = new cb = new DataTable = new = = "Modified";

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

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

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

Оптимальное решение Ч гибрид двух этих методов. При необходимости при ложение явно открывает соединение с БД. Это напоминает способ, при котором объекты DataAdapter управляют состоянием соединения. Приложение при запус ке выполняет такой код:

Visual Basic = Orders) Value = 518 Часть IV Создание эффективных приложений с использованием Visual C# = = strCustomerlD;

При каждом метода неявно открывается и закрывается связанный с объектом объект Connection. Это означает, что код че тырежды открывает и закрывает объект Connection. Вызвав метод перед вызовом методов DataAdapterfill, вы повысите производительность приложения. Кроме того, явно открыв объект Connection до вызова методов объек тов вы сможете объединить ожидающие передачи в БД, в одну транзакцию.

Если бы мне пришлось рекомендовать единый, универсальный способ управ ления состоянием соединения, я порекомендовал бы способ, описанный выше.

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

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

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

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

Пул соединений предназначен преимущественно для многоуровневых прило жений, но он годится для повышения производительности и простых двухуров невых приложений. Когда наше приложение-пример явно или неявно закрывает объект Connection, реальное соединение с БД помещается в пул. Если приложе ние, до того как истечет время хранения соединения в пуле, снова откроет объект соединение будет использовано повторно.

Чтобы при работе с поставщиком OLE DB Data Provider не применять в своем приложении пул соединений, добавьте в подключения такой атрибут:

OLE OB Services=-4;

Если требуется исключить обращение к пулу соединений при работе с постав щиком SQL Client Data Provider, добавьте следующий атрибут:

Pooling=False;

13 Создание эффективных Windows-приложений Работа с данными больших двоичных Для повышения производительности можно хранить данные больших двоичных объектов в файлах на сервере, а пути к этим файлам Ч в БД. Опе рационные системы лучше применять для работы с файлами. Хранить те же дан ные в БД менее эффективно. Например. SQL Server 2000 делит BLOB-данные объе мом свыше 8 на несколько страниц БД. Таким файл размером делится на пять составных частей.

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

Если вы решите хранить BLOB-данные в БД, в следующих разделах приводит ся несколько советов по работе с такими данными в ADO.NET.

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

Один из способов повысить производительность приложения Ч отложить выборку BLOB-данных из БД до момента, когда они понадобятся. Выбирайте ные данные заблаговременно, а затем по мере необходимости получайте BLOB данные. Этот способ особенно полезен, если пользователь обращается только к отображаемой в настоящий момент записи, Обработка BLOB-данных с помощью объектов DataSet На самом деле обращаться и редактировать содержимое столбцов с BLOB-данны ми при помощи объектов DataSet очень просто. ADO.NET хранит BLOB данные в виде строк, а двоичные Ч в виде байтовых массивов. В отличие от пре дыдущих моделей к данным объект не предоставляет Вам потребуется и редактировать все содер жимое поля.

Работают с содержимым поля с текстовыми BLOB-данными так же, как и с содержимым других полей с текстовыми типами данных.

Visual Basic Dim As DataRow Dim strBlob As String к содержимому поля с текстовыми strBlob = содержимого поля с текстовыми BLOB-данными = strBlob 520 Часть IV Создание эффективных приложений с использованием Visual C# DataRow string к с текстовыми = (string) содержимого поля с текстовыми BLOB-данными = strBlob;

К полям с двоичными BLOB-данными обращаются точно также, как и к полям, содержащим двоичные данные меньшего объема.

Visual Basic Dim row As DataRow aBinaryBlob As к содержимому с двоичными BLOB-данными aBinaryBlob = ByteO) содержимого поля с двоичными BLOB-данными aBinaryBlob Visual DataRow aBinaryBlob;

к с двоичными BLOB-данными aBinaryBlob = содержимого поля с двоичными = aBinaryBlob;

Обработка с помощью объектов DataReader Объект предоставляет обращаться сразу ко всему содер жимому поля с или выбирать это содержимое по фрагментам.

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

Visual Basic Dim As Dim As Dim As Integer Dim strTextBlob As Dim aBinaryBlob As ByteO rdr = Do While StrTextBlob aBinaryBlob = ByteO) Loop ГЛАВА 13 эффективных Windows-приложений Visual C# cmd;

OleDbDataReader string Byte[] = while { strTextBlob = aBinaryBlob = I Примечание Этот фрагмент кода получает содержимое поля с используя метод со строгим контролем типов.

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

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

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

Объект DataReader предоставляет два метода, и позволяю щих выбирать двоичные данные фрагментами. Следующий фрагмент кода с по мощью метода GetBytes выбирает из объекта DataReader двоичные фрагментами по 8 кбайт и записывает их в файл. Используя эту же логику, вы по лучите текстовые ВЮВ-данные с помощью метода Visual Basic в начало модуля кода следующую строку Imports Dim cmd As OleDbCommand Dim As OleDbDataReader Dim As Integer = Dim As Integer = Dim intOffset As Integer = Dim As Integer Dim As Byte Dim As String = Dim As New rdr = 522 Часть IV Создание эффективных приложений с использованием Do intBytesReturned = intOffset, _ 0, If (intBytesReturned > 0) 0, intBytesReturned) End If intOffset += Loop Until о intChunkSize Visual C# в начало кода следующую строку using rdr;

int intBinaryBlobCol = 1;

int intChunkSize = 8192;

int intOffset = 0;

int intBytesReturned;

aBinaryBlob = = = new = do { intBytesReturned = (int) intOffset, aBinaryBlob, 0, intChunkSize);

if (intBytesReturned > 0) 0, intBytesReturned);

intOffset intBytesReturned;

1 while (intBytesReturned == intChunkSize);

rdr.CloseO;

Двоичные в БД Northwind вероятно, что БД Northwind включает столбцы с в таблице Employees есть столбец содержащий фотографию сотрудника.

К сожалению, столбец Photo также содержит некоторые дополнительные дан ные, представляющие собой Access. Этот заголовок позволяет Access определить, данные какого типа содержит Ч.jpg-файл, документ Word, электронную таблицу Excel и т.д. Как следствие, если вы попытаетесь выбрать содержимое столбца Photo средствами ADO.NET (или ADO, RDO и др.), то не смо жете загрузить эти данные в элемент управления или просмотреть содержимое файла в программе для работы с изображениями, например Paint.

ГЛАВА 13 Создание эффективных Windows-приложений Как же отбросить OLE-заголовок Access и оставить только нужные вам данные.

Если вкратце, никак Формат OLE-заголовка Access Ч оригинальная разработка Microsoft, к которой нет документации.

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

Кроме данное приложение можно рассматривать как пример загрузки содержимого файлов в БД средствами параметризованных запросов, Пример приложения для работы с Теперь, когда в Employees есть реальные изображения, мы вкратце рас пример приложения, возвращающего и выводящего эти на Приложение (рис. 13-11), записанное на прилагаемом к книге компакт-диске, возвращает сведения о сотруднике из БД Northwind в объект Рис. 13-11. двоичных в элементе управления В целях быстрого создания приложения я воспользовался объектом Manager и связанными элементами управления для контроля текущего номера и общего числа записей, а также для упрощения вывода сведений о сотрудниках.

Используемый в приложении объект включает два отдельных объекта DataTable: один для BLOB-данных и один Ч для прочих данных. Дочерний объект также включает поле первичного ключа упрощающее переход от записи родительского объекта к соответствующей записи дочернего объекта DataTable. Кроме того, я добавил в родительский объект Table столбец FetchedPhoto, позволяющий определить, выбрана ли из БД фотогра фия конкретного сотрудника.

При запуске приложение получает из БД стандартные сведения о сотрудниках полей EmployeelD, LastName, FirstName и Затем, когда 524 Часть IV эффективных приложений с тель впервые обратится к записи о конкретном сотруднике, приложение считы вает двоичные Ч содержимое поля (фотографии имеют не большой размер Ч всего 22 Ч и поэтому загрузка изображений при запус ке приложения не оказала бы резко отрицательного влияния на производитель ность. Это особенно верно, если размер таблицы, из которой выбираются данные, невелик). Такой способ значительно повышает производительность приложения, выводящего на экран лишь часть возвращаемых записей.

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

Ответ. Я связывание с данными при создании пользовательского ин терфейса приложения, чтобы сделать это быстро и с минимальным объемом кода.

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

Вопрос. Можно ли связать элементы управления с объектом DataSet конт роля типов?

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

ГЛАВА 13 Создание эффективных Windows-приложений Visual Basic элемента управления с объектом DataSet, управления с объектом DataTable = DataSet = Visual элемента управления TextBox с объектом DataColumn DataSet, элемента управления DataGrid с объектом DataTable = DataSet;

= "TableName";

Вопрос. При запуске приложения мне нужно загружать данные. Ваши рекомен дации?

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

Вопрос. Почему в коде на этапе разработки приложения-примера положение текущего заказа определяется с помощью объекта а не Ответ. Если пометить объект как он по-прежнему будет находиться в наборе Rows объекта DataTable. Приложение позволяет помечать заказы на и если бы в нем использовался только объект DataTable, по требовалось бы на порядок больше кода, чтобы при перемещении по оставшим ся заказам пропускать заказы, помеченные как удаленные.

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

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

526 Часть IV Создание эффективных приложений с использованием Ответ. Я уже говорил, что пессимистическое управление блокировками на самом деле требуется лишь ограниченному кругу приложений. Определенно, пессимис тическое управление блокировками в многоуровневом приложении, промежуточ ный уровень которого не поддерживает сведений о состоянии Ч трудная задача.

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

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

Тем не менее для реализации этой функциональности разработать соб ственную схему Если честно, мне не приходилось развертывать мно гоуровневые приложения, на такой архитектуре. Но если бы мое су ществование зависело от разработки подобных приложений, обращающихся к СУБД SQL Server, я бы поступил следующим образом.

Настроил параметры БД таким образом, чтобы могли изменять содержимое таблиц только посредством вызовов хранимых процедур.

2. Добавил в таблицу два столбца: один с уникальным ключом блокировки, а дру гой Ч с датой и временем успешного наложения блокировки пользователем, 3. Создал хранимую процедуру, позволяющую налагать блокировку на ряд дан ных. Хранимая процедура принимает в качестве параметров ввода первичный ключ ряда, а также Если на ряде нет блокировки, процедура помечает его как заблокированный. Вот пример такой процедуры:

CREATE uniqueidentifier) AS UPDATE SET LockAcquired = = WHERE ID = AND LockAcquired IS NULL 4. Создал хранимую позволяющую редактировать содержимое ряда.

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

Успешно обновив ряд, процедура снимает с него блокировку.

CREATE PROCEDURE int, uniqueidentifier) AS UPDATE tblPessimisticLock SET DescCol = WHERE = AND LockID = IF = ГЛАВА 13 Создание эффективных SET NOCOUNT ON UPDATE SET = NULL, LockID = NULL WHERE ID = AND LockID = END 5. Создал хранимую процедуру, позволяющую снять с ряда заданную блокировку.

CREATE PROCEDURE int, AS SET LockAcquired = NULL, LockID = NULL WHERE ID = AND LockID = 6. задание, снимающее все неснятые вовремя блокировки. Следующий запрос ищет ряды с блокировкой, удерживаемой более 5 и ее:

UPDATE SET LockAcquired = NULL, LockID = NULL 5, LockAcquired) <= GetDate{) Создание эффективных Web-приложений того как мы обсудили разработку Windows-приложений для доступа к дан ным, пришла пора рассмотреть их Интернет-аналог Ч Краткий обзор Web-приложений Эта глава познакомит вас с Web-приложениями. О технологии ASP.NET написано множество книг, в том числе и посвященные написанию кода для досту па к данным, например Building Web Solutions with ASP.NET and (Microsoft Press, Изучив материал этой главы, вы получите базовые навыки создания Web-приложений, взаимодействующих с БД при помощи Вы также уз наете о связывании с данными, преимуществах и недостатках различных функ ций кэширования разбивке информации и передаче об новлений. В большинстве приводимых примеров используется наиболее мощный и гибкий элемент управления ASP.NET, связанный с данными, Ч DataGrid.

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

ASP.NET значительно упрощает процесс создания Web-приложений. ASP.NET код можно писать на языке по вашему выбору Visual Basic или Visual ГЛАВА 14 Создание эффективных Web-приложений Достаточно задать свойства Web-элементов управления как в случае с обычными управления, и элементы автоматически преоб разуют эти параметры в HTML-код. Если поместить на Web-форму элемент управ ления Button ASP.NET и добавить код в событие Click этого элемента, ASP.NET ав томатически добавит в соответствующую Web-страницу HTML-код, который по зволит Web-серверу по кнопки выполнять код ее события Click. Благодаря метаданным, которые ASP.NET добавляет в код может обращаться к па раметрам элементов управления и собирать отправленные пользователем данные.

Многие создававшие приложения на ASP.NET, не знают о воз можностях ASP.NET, упрощающих разработку Web-приложений. А между тем это Ч один из китов мощи ASP.NET. Так, некоторые разработчики не вполне понимают, что код на ASP.NET и пользовательский HTML-интерфейс выполняются на разных машинах. Еще больше программистов даже представления не имеют о том, что промежуточный уровень приложений ASP.NET не поддерживает сведения о состо янии, а часть разработчиков даже не знает, что такое промежуточный уровень без поддержки сведений о состоянии (stateless).

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

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

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

Скажем, вы написали на ASP.NET код, который при помощи объекта выбирает из БД о товаре в объект Кроме вы связали с объектом DataTable Web-элемент управления чтобы сведения о товаре на Web-странице. Предположим также, что вы реализовали на Web-стра нице кнопку или ссылку, позволяющую товар в корзину.

Среда ASP.NET помогла вам преобразовать результаты запроса в HTML-код, который был выведен в Web-браузере. Однако сразу после ответа на запрос стра ницы ASP.NET освободила ресурсы этой страницы, включая объект DataTable с информацией о товаре. Web-браузер выводит только визуальное представление результатов и в действительности не получал объекта DataTable. Фактически, даже 530 Часть Создание эффективных приложений с использованием если бы он его и получил, то не знал что с этим объектом делать;

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

Использование доверенных соединений В предыдущих главах мы применяли доверенные соединения для подключения к локальному экземпляру Framework SDK Initial Эту же строку подключения можно вставить в Web-приложение и в период разработки, но тогда возникнут проблемы в период выполнения. Скажем, вы созда ли новое приложение и добавили в событие Load Web-формы такой код:

Visual Basic Dim As String = & "Initial Dim As New Visual C# string strConn;

strConn = + "Initial = new cn.OpenO;

В от версии ОС Windows может при запуске такого Web-прило жения сгенерировать исключение и выдать следующее сообщение:

Login failed for Используемая строка подключения отлично работала в Windows- и в консоль ных приложениях. Но что же не так в случае с Web-приложением? Помните: если используются доверенные соединения, SQL Server определяет наличие у вас на подключение к БД на основе ваших реквизитов для входа в сеть. Сообщение, указывает, почему именно невозможно подключить ся к БД. При подключении применяются реквизиты учетной записи ASP.XET, а не ваши реквизиты, поскольку код выполняется в процессе ASP.NET. Результат попытки подключения зависит от того, имеет ли учетная запись ASP.NET соответствующие права в БД SQL Server или нет.

ГЛАВА Создание SQL Server Enterprise Manager упрощает назначение учетным записям прав доступа к БД. Раскройте узел Security нужного сервера, щелкните узел Logins пра вой кнопкой и выберите New Login. Если вы работаете с БД MSDE и у вас нет до ступа к Enterprise для предоставления учетной записи доступа к БД SQL Server можно выполнить такой запрос:

exec Подмена пользователей Что, если вы хотите подключаться к БД, применив реальные реквизиты пользова сильно упрощает подмену пользователей. Создав в Visual Studio новое Web-приложение, в окне Solution Explorer вы увидите файл \Veb.config. Это XML-документ с параметрами вашего приложения. В данном документе есть раз дел авторизации:

/> Такие параметры позволяют всем пользователям подключаться к приложению.

Если их изменить, как показано приложение станет подменять текущего пользователя и исключит анонимные подсоединения:

/> После этого при подключении к БД по доверенным соединениям при меняться реальные реквизиты пользователя для входа в сеть. Тем не менее по ряду причин мне не нравится такой способ работы с доверенными соединениями в Web приложениях, Данная архитектура совершенно не использует преимущества пула соедине ний. Если к Web-приложению, в котором задействованы подмена и доверенные соединения, подключатся три разных каждое из соединений будет иметь разный контекст безопасности. Как не удастся помес тить соединение Пользователя А в пул и затем задействовать это соединение для Пользователя Б. Фактически пул соединений может оказать негативное влияние на производительность приложения, применяющего доверенные соединения.

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

Мне не реализовывать безопасность в приложении средствами Если вы знакомы с доверенными соединениями с БД, заманчиво использовать их, а не изучать различные варианты проверки подлинности, предоставляемые ASP.NET.

Вам не надо лишний раз обращаться по сети к БД для проверки подлинности текущего ASP.NET предлагает множество способов реализации бе зопасности в приложении: проверку подлинности средствами Windows, 532 Часть IV Создание эффективных приложений с использованием подлинности средствами службы Microsoft Passport, а также проверку подлин ности средствами Forms.

Работа с БД Access Многие разработчики имеют дело с БД Access и планируют использовать их в своих Я это не одобряю. БД Access не рассчитаны на множество параллельно работающих пользователей. Их масштабирования да леки от БД SQL Server и Oracle. Тем не менее привлекательность БД Access вполне понятна. Создавать их и управлять ими очень просто.

Итак, не рассказав хотя бы вкратце о работе с БД Access в ASP.NET-приложени ях, я оказал бы вам медвежью услугу. На поздних этапах разработки специа листы Microsoft изменили сетевые разрешения, назначенные учетной записи ASP.NET. Эти изменения напрямую затрагивают разработчиков, использующих в своих ASP.NET-приложениях БД Access.

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

Microsoft database engine cannot open the file It is already opened exclusively by another or you need permission to view its data.

или Operation must use an updateable query.

В этом случае проблема скорее всего в том, что у учетной записи ASP.NET нет права доступа на запись к файлу блокировок. Убедитесь, что учетная запись ASP.NET обладает необходимыми разрешениями на доступ к папке, в которой находится БД Access.

Вывод данных на Web-странице Конечно, подключение к БД Ч это только первый этап. После подключения обычно выполняют запрос, получают его результаты и выводят полученные данные на Web странице.

Как и в случае с Windows Forms, написать ASP.NET-код для задания свойств элементов управления можно вручную:

Visual Basic TextBoxl.Text = Visual C# TextBoxl.Text = (string) ГЛАВА 14 Создание эффективных Web-приложений Чтобы упростить данный процесс, воспользуйтесь функциями связывания с данными, которые предоставляют элементы ASP.NET. Скажем, вы до бавили в Web-приложение код, выполняющий запрос и его резуль таты в объект Теперь вам нужно вывести содержимое на Web-стра нице в виде HTML-таблицы. Можно генерировать HTML-код таблицы, для этого большой объем кода на ASP.NET, можно воспользоваться XML и XSLT, но вы избавите себя от массы проблем, если задействуете средства связывания с данными, предоставляемые ASP.NET.

Связанные элементы управления ASP.NET преобразуют данные в HTML-код для Web-страницы. Связывание с данными в ASP.NET Ч это улица с односторонним движением. Если связать текстовое поле с полем объекта страницу и изменить содержимое текстового поля, это никак не отобразится на оригинальном содержимом поля в объекте DataTable. У элемента управления есть возможности, позволяющие реализовывать в Web-приложениях функции обновления, однако большинство связанных элементов управления ASP.NET применяются для вывода данных, доступных только для чтения.

Как и при связывании с данными в Windows, с источниками данных разреша ется связывать однозначные, например текстовые и многозначные элемен ты управления, например сетки. Для связывания многозначных элементов управ ления применяются стандартные свойства и Связать с данными одновременно однозначные и многозначные элементы управления уда ется при помощи статичного объекта Web-страницы, Использование метода Начнем изучение возможностей связывания с данными, предоставляемых ASP.NET, с того, что свяжем с данными элемент управления воспользовавшись методом Eval объекта DataBinder. DataBinder получает данные из объектов при помощи механизма Reflection. Так, следующий код возвращает значение поля Company-Name первой записи объекта DataTable:

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

Показанный ранее метод Eval перегружен. Есть еще один метод прини мающий строку формата. Скажем, вы получаете информацию о конкретном то варе и вам вывести Б текстовом поле цену этого товара. Следующий вызов метода Eval форматирует содержимое поля как значение с типом дан ных currency:

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

следующий код возвращает значение свойства Visible элемента управления TextBox:

534 Часть IV Создание эффективных приложений с использованием "Visible") Данный код примерно эквивалентен прямому обращению к свойству Visible элемента управления TextBox за исключением что в коде на основе используется позднее связывание. Если вы случайно сделаете и напишете такой код:

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

Связывание элемента TextBox с объектом DataSet Получив представление об объекте давайте воспользуемся им и свя жем элемент управления TextBox с полем данных, которое возвращает объект Следующий фрагмент кода выбирает данные из таблицы Customers в объект и затем при помощи объекта DataBinder задает свойству Text элемента управления TextBox значение поля первой записи, воз вращенной запросом:

Visual Basic Dim strConn, As String = & "Initial strSQL = "SELECT FROM Customers & CustomerlD Dim da As New strConn) Dim As New strExpression = TextBoxl.Text = Visual C# strConn, strSQL, strExpression;

strConn = "Initial strSQL = "SELECT CustomerlD, CompanyName Customers " + "WHERE CustomerlD = da = new strConn);

DataSet ds = new = TextBoxl.Text = (string) strExpression);

ГЛАВА 14 Создание эффективных Web-приложений Связывание элемента управления TextBox с объектом С помощью объектов удается также получать данные из объектов Data Reader. Следующий фрагмент кода получает результаты запроса при помощи объек та DataReader. Поскольку нам нужна только первая запись из этих результатов, в коде используется константа из перечисления Visual Basic Dim strConn, As String & "Initial Dim As New strSQL = "SELECT FROM Customers & "WHERE CustomerlD = Dim As New Dim rdr As = Visual C# string strConn, strSQL;

strConn = + "Initial = new strSQL = "SELECT CustomerlD, CompanyName FROM Customers " + "WHERE CustomerlD = cmd new OleDbDataReader = = (string) cn.CloseO;

Связывание элементов управления DataGrid с результатами запросов NET включает элемент управления DataGrid, позволяющий пре образовывать результаты запроса в HTML-таблицу. Данный элемент ет ряд мощных функций, например постраничное представление данных, сорти ровку и Сейчас же я расскажу о том, как при помощи элемента уп равления DataGrid отобразить результаты 536 Часть IV Создание эффективных приложений с использованием Связывание элемента управления с объектом DataSet Следующий фрагмент кода создает объекты и выбира ет результаты запроса в объект DataSet и связывает элемент DataGrid Web-формы с нужным объектом из состава DataSet.

Visual Basic Dim As String strConn = " & "Initial strSQL "SELECT Phone & Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New "Customers") = ds = "Customers" g Visual C# string strConn, strSQL;

strConn = + "Initial strSQL = "SELECT ContactName, Phone + Customers";

OleDbDataAdapter da = new strConn);

DataSet ds = new DataSetO;

= ds;

= "Customers";

Данный код идентичен коду для связывания элемента управления DataGrid с объектом DataSet за одним небольшим, но важным исключе нием. Задав значения свойств DataSource и DataMember, код вызывает метод Data Bind элемента управления DataGrid. В случае с Windows-формой DataGrid связы вается с источником данных сразу после задания значений указанных свойств. В случае с Web-формой дело обстоит иначе. DataGrid отобразит данные из источ ника только после вызова метода этого элемента управления. Кроме того, можно вызвать метод страницы;

при этом неявно вызываются одноимен ные методы элементов управления этой страницы.

Связывание элемента управления DataGrid с объектом Как уже говорилось, данные на Web-странице доступны только для чтения. Объект DataSet предоставляет значительно более широкую функциональность (кэширо вание простое перемещение между таблицами и которая может потребоваться вам при выполнении запроса и выводе его результатов на Web странице. Для повышения производительность элементы управления ASP.NET также поддерживают связывание с объектами DataReader.

ГЛАВА 14 Создание эффективных Web-приложений Следующий фрагмент кода выполняет все тот же запрос, но получает его ре зультаты при помощи объекта и связывает с этим объектом эле мент управления Web-формы.

Visual Basic Dim strSQL As String = _ "Initial strSQL = Phone " & Customers" Dim As New Dim As New Dim As OleDbDataReader = = g Visual C# string strConn, strSQL;

strConn = + "Initial strSQL = "SELECT CompanyName, ContactName, Phone + Customers";

OleDbConnection = new cmd = new strConn);

OleDbDataReader = = rdr;

gridCustomers.

Кэширование данных между обращениями к БД Итак, вы умеете выводить данные на Вы знаете, как выполнить запрос и преобразовать его результаты в HTML-код при помощи элементов управления, связанных с данными. Что, если вы не хотите выполнять одинаковые запросы при каждом обращении к серверу? Например, можно кэшировать результаты запроса, интенсивно использующего ресурсы системы. Или же сохранять содержимое кор зины клиента.

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

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

Способ без хранения сведений о состоянии Нет закона программирования, что необходимо данные.

Web-приложения может исключать хранение све дений о состоянии.

При этом выполнение и преобразование их результатов в HTML осу ществляется средствами связанных элементов управления. Если у вас есть прило жение размещения заказов, в котором пользователь хранит список товаров в корзине, допустимо записывать содержимое корзины текущего пользователя в БД, выполняя поиск связанных с этим пользователем записей БД по идентификатору сеанса.

Преимущества Достоинства данного способа таковы: если исключить в ASP.NET-кодс кэширова ние данных, на каждого клиента приложению потребуется минимальный объем памяти. С точки зрения требований к памяти этот обеспечивает масштабируемость.

Недостатки Тем не менее это слишком упрощенный взгляд на создание масштабируемых при ложений. Скажем, ваше приложение Ч интерактивный каталог, на каждой стра нице которого отображается список категорий товаров. Стоит ли получать этот список из БД каждый раз, когда обращается к странице каталога?

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

Файлы cookie Многие Web-узлы хранят пользовательские данные на стороне клиента при по мощи файлов cookie. Объекты Request и Response ASP.NET предоставляют набор cookies, позволяющий записывать и получать информацию между обращениями к БД. Так, следующий код с помощью файла cookie отслеживает время и дату пос леднего посещения узла пользователем:

Visual Basic If Is Nothing Then = "This is first visit! Welcome!" ГЛАВА 14 Создание эффективных Web-приложений Else = "Welcome back. last visit was: & _ = End If Visual C# if == null) { IblLastVisit.Text = "This is first visit!

} else { IblLastVisit.Text = "Welcome back. Your last visit was: + = !

Преимущества ASP.NET упрощает работу с файлами cookie. Хранение данных на стороне клиен та позволяет не поддерживать сведения о состоянии, благодаря чему повышается масштабируемость. Кроме того, файлы cookie разрешается Для управления сроком хранения файлов cookie применяют свойства Expires объекта Недостатки Файлы cookie рассчитаны на незначительный объем информации. Большинство браузеров не позволяют хранить в файле cookie больше пары килобайт данных, и поэтому для кэширования результатов запроса, возвращающего больше пары такие файлы не особенно удобны.

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

безопасности файлов cookie достаточно низок. Пользователь право изменить содержимое такого файла.

Скрытые поля допустимо хранить в скрытом поле Web-страницы. Данный чем-то использованию скрытого элемента управления Windows-формы.

Тем не менее обращаться к скрытым полям из кода на ASP.NET не так-то просто. В связи с этим, вместо того чтобы подробно останавливаться на преимуществах и недостатках, мы рассмотрим похожее средство, более удобное для разработчика Ч свойство 540 Часть IV Создание эффективных приложений с использованием Свойство Класс Page из пространства имен System.Web.fJI предоставляет свойство ViewState, содержащее объект По сути, данный объект аналогичен набору пар лимя Ч значение и, так же как и набор Cookies объектов и Response, применяется для хранения информации, Записав данные в свойство ViewState страницы, вы получите их при следую щем событии передачи данных серверу, связанном с этой страницей. Работать со свойством ViewState так же просто, как и с файлами cookie:

Visual Basic If Is Nothing Then IblLastVisit.Text = "This is first visit! Welcome!" ViewStateC'LastVisit") = Now.ToString Else IblLastVisit.Text = "Welcome back. Your last visit was: & ViewStateC'LastVisit") = Now.ToString End If Visual C# if == null) Х IblLastVisit.Text = "This is first visit!

= else { IblLastVisit.Text = "Welcome back. Your last visit was: " + = DateTime.Now.ToStringO;

a ' visual 7.0" body 101;

LEFT: POSITION: TOP: aaek.

Рис. 14-1. Свойство страницы ГЛАВА 14 Создание эффективных Web-приложений На рис. показан исходный код страницы, которая с помощью фрагмента кода отслеживает в свойстве время и дату последнего посе щения страницы пользователем. Внимательно изучив HTML-код страницы, вы таи дите, что свойство ViewState хранится в скрытом поле. Поскольку независимо от конфигурации все браузеры поддерживают скрытые поля, свойство ViewState го дится для хранения данных в различных ситуациях, когда задействовать файлы cookie проблематично.

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

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

Свойство ViewState не рассчитано на хранение любых данных, а только тех, которые ASP.NET может сериализовать. Разрешается записывать значения с про стым типом данных, например строки и целые числа, а вот с универ сальным типом данных Object записать нельзя, поскольку ASP.NET не умеет сохра нять и создавать экземпляры таких объектов. В свойство ViewState можно запи сывать классы, поддерживающие интерфейс например объекты и Хранение сведений о состоянии на стороне Web-сервера ASP.NET также предоставляет различные параметры хранения сведений о состоя нии на стороне Web-сервера.

Свойство Session У класса Page есть свойство Session, возвращающее экземпляр класса Как и в случае со свойством ViewState. в свойстве Session страницы разрешается хранить данные. Однако они хранятся на стороне сервера, а не передаются бра узеру вместе с HTML-кодом страницы.

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

542 Часть Создание эффективных приложений с использованием Преимущества Поскольку хранение данных осуществляется Web-сервером, а не браузером, га рантируется защита этих данных. Пользователю не удастся средствами браузера просмотреть или изменить содержимое объекта Session. Кроме того, поскольку данные хранятся на сервере, объект Session можно использовать независимо от параметров клиента.

Недостатки Хранение сведений о состоянии с использованием свойства Session снижает мас штабируемость кода на ASP.NET, поскольку при этом для каждого сеанса прило жения требуются определенные ресурсы.

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

50? 500? Теперь умножьте этот объем на число клиентов, использующих данную вашего Web-приложения. 0 клиентов? 100? Еще больше? Оце ните общий объем ресурсов сервера, который понадобится вам при хранении све дений о состоянии с использованием свойства Session.

Объект Application Объект Application аналогичен Session за исключением того, что данные, доступные через свойство страницы, совместно используются всеми сеансами. Таким образом, данные объекта доступны всем клиентам.

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

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

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

Объект Cache Объект Page предоставляет свойство Cache, возвращающее экземпляр объекта Cache.

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

Х разрешается указать время удаления элемента из кэша, задав конкретное Time) или относительное значение;

ГЛАВА 14 эффективных Web-приложений Х можно задать значение свойства чтобы при изменении за элемента добавленный элемент данных удалялся из кэша. Или же определить значение свойства чтобы элемент удалялся из кэша при редактировании содержимого конкретного XML-файла;

Х допустимо указать функцию обратного вызова, к которой обратится ASP.NET при удалении элемента из кэша;

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

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

Эти данные можно выбрать в объекте и поместить его в объект Appli cation. Таким образом, не придется каждый раз выбирать эти дан ные при выводе новой страницы. это значительно снизит объем сете трафика, однако при выводе новой страницы вы будете по-прежнему пре образовывать содержимое DataSet в HTML-код.

ASP.NET предлагает изящное решение данной проблемы: кэширование выво да. Можно вывод, представляющий страницу или ее часть. Или же полностью или частично кэшировать вывод с кодом страницы на основе пара метров. Если вам нужно кэшировать HTML-данные с кодом странице, не преми ните воспользоваться этой мощной функцией ASP.NET.

Преимущества Если необходимо многократно генерировать один и тот же HTML-код на основе содержимого объекта DataSet, кэширование вывода окажется очень эффективным, поскольку требует меньше ресурсов сервера. Как и в случае с объектом при кэшировании вывода можно управлять сроком хранения данных.

Недостатки Как и все серверные функции кэширования, кэширование вывода требует ресур сов сервера.

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

Представьте, что вашему приложению нужно выполнить настолько сложный или длительный по времени запрос, что вы предпочли бы сохранить результаты этого запроса в таблице, а не выполнять его каждый раз, когда пользо ватель обращается к следующей странице результатов. Например, пользователь может выполнить в БД поиск приводов CD-RW со скоростью записи не менее 544 Часть IV Создание эффективных приложений с использованием которые поддерживают интерфейсы и и продаются в магазинах, расположенных не далее, чем в 30 милях от дома пользователя. Вот образец та кого запроса:

SELECT ProductID, Description,... FROM Products WHERE UnitPrice < 200 AND Description LIKE AND Description LIKE AND...

Представьте, что запрос возвращает 50 записей, на каждой странице отобра жается 10 товаров. Можно выполнять данный запрос каждый раз, когда пользова тель открывает новую страницу результатов, а можно поместить результаты это го запроса в одну из таблиц БД. Воспользовавшись запросом INSERT INTO и иден тификатором сеанса пользователя, чтобы отслеживать принадлежность результатов к определенному следует создать вторую где будут храниться результаты запросов к первой таблице:

INSERT INTO SELECT ? AS SessionID, ProductID, Description, UnitPrice,...

FROM Products WHERE UnitPrice < 200 AND Description LIKE AND Description LIKE AND...

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

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

Примечание Решив воспользоваться описанной техникой, не забудьте удалить записи из БД в событии End объекта Session или когда это потребуется.

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

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

Недостатки Хранить сведения о состоянии в БД сложнее, чем просто хранить данные в про стых объектах и наборах типа Session, Application, или в файле cookie, ГЛАВА 14 Создание эффективных Web-приложений Рекомендации по хранению сведений о состоянии То, как и где вы храните сведения о состоянии, может значительно влиять на производительность, масштабируемость и безопасность ваших Web-приложений.

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

Хранение данных в объектах ViewState Применяйте объекты ViewState, когда имеете дело с небольшими наборами татов и допускаете просмотр этих результатов пользователем. Если же необходи мо обеспечить защиту данных и исключить возможность изменения информации пользователем, храните данные в объекте Session или в БД. Помните: при обра щении к ним данные из объекта каждый раз передаются от клиента к серверу и обратно. Чем больше размер содержимого объекта ViewSession, тем боль ше уйдет на передачу его по сети.

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

Хранение данных в объекте Session Объект Session рассчитан на хранение небольшого объема данных, критичных для конкретного сеанса, а также предназначен для случаев, когда хранение данных в объекте ViewState соображениям безопасности. Помните: чем больше данных в объекте тем сильнее и в несколько раз быстрее по срав нению с объектом Application падает производительность. Казалось бы, всего 100 кбайт данных в объекте Session. Но умножьте это значение на число сеансов, поддерживаемых ASP.NET в конкретный момент времени! Рекомендую вам хранить большой объем данных, уникальных для отдельного сеанса, в БД, Хранение данных в БД Храните большой объем данных, уникальных для отдельного сеанса, в БД, Понятно, что обращаться к таким данным, находящимся в памяти, быстрее, чем получать их из БД. Но если вам требуется хранить большие объемы данных, специфичных для отдельных сеансов, эти данные в БД, вы уменьшите объем памяти сервера, используемый вашим приложением в данный конкретный момент вре мени.

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

Часть IV Создание эффективных приложений с использованием Постраничная разбивка информации каталог товаров которых может уместиться на одной Web-странице.

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

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

Средства постраничного представления информации, предоставляемые Web-элементом управления DataGrid Web-элемент управления DataGrid средства, значительно упрощающие постраничный вывод результатов запроса. Свойства и это го элемента позволяют программно управлять тем, где и как будут выводиться данные, с которыми связан DataGrid. На рис. 14-2 показана Web-страница, созданная с использованием связанного с данными элемента управления DataGrid и его средств постраничного представления информации.

Рис. 14-2. Web-страница, использующая средства постраничного представления информации, предоставляемые элементом управления DataGrid Разработчикам, создающим Web-приложения при помощи Visual Studio проще всего задать эти свойства средствами диалогового окна Property Builder элемента управления DataGrid. Чтобы запустить его, щелкните на Web-форме эле ГЛАВА 14 Создание эффективных Web-приложений мент управления DataGrid правой кнопкой и выберите в контекстном меню ко манду Property Builder. Затем в правой панели открывшегося окна ссылку Paging. Откроется диалоговое окно, аналогичное показанному на рис. 14-3.

Рис. Задание параметров постраничного представления информации для управления DataGrid при помощи диалогового окна Property Builder Свойство Задавая свойству значение True, вы указываете элементу управления DataGrid создать ссылки для перемещения между страницами набора результатов, Если значение свойства Ч True, сразу после связывания с источником данных элемент управления DataGrid автоматически создает вместе со страницами результатов ссылки для перемещения между этими страницами. Можно указать нужный вид ссылок Ч кнопки Next (предыдущая страница) и Previous щая страница) или номера, позволяющие перейти к конкретным страницам.

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

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

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

Следующий фрагмент кода помещает в объект только первые 10 записей набора результатов и задает соответствующим свойствам элемента 548 Часть Создание эффективных приложений с использованием DataGrid нужные значения для создания ссылок на другие на бора результатов.

Visual Basic = True = = gridResults.PageSize = = = PagerPosition.TopAndBottom Dim As String strConn = & _ Dim As New StrSQL = "SELECT FROM Customers" Dim As New 01eDbCommand(strSQL, cnNorthwind) strSQL = "SELECT TOP 10 Country & _ "FROM Customers" Dim As New cnNorthwind) = Dim As OleDbDataReader = gridResults.DataSource = rdrOnePage Visual C# = true;

gridResults.AllowCustomPaging = true;

gridResults.CurrentPagelndex = 0;

= 10;

gridResults.PagerStyle.Mode = = string strConn, strSQL;

strConn = + "Initial OleDbConnection cnNorthwind = new strSQL = "SELECT Customers";

cmdFetchRowCount = new cnNorthwind);

strSQL = "SELECT TOP 10 CustomerlD, CompanyName, ContactName, Country Customers";

OleDbCommand cmdFetchOnePage = new cnNorthwind);

= ГЛАВА 14 Создание эффективных Web-приложений = = rdrOnePage;

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

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

Скажем, вам требуется выводить 10 записей на странице. Тогда для вывода за писей пятой страницы нужно пропустить первые 40 записей и выбрать следую щие 10. Приведенный ниже фрагмент кода помещает в объект DataSet только за писи пятой страницы:

Visual Basic Dim As String = & "Initial strSQL = "SELECT ContactName, Country & _ Customers" Dim da As New 01eDbDataAdapter(strSQL, strConn) Dim ds As New 40, 10, "Customers") Visual C# string strConn, strSQL;

strConn "Initial strSQL = "SELECT CustomerlD, CompanyName, ContactName, Country + Customers";

OleDbDataAdapter da = new DataSet ds = new DataSetO;

40, 10, Этот фрагмент очень прост, но сам подход к решению проблемы имеет чительный недостаток. В этом примере БД все равно возвращает данные всех записей таблицы. По-прежнему имеется объективное падение производительно сти, связанное с выборкой 40 записей, которые объект просто отбра сывает и не помещает в объект DataSet.

550 Часть Создание эффективных приложений с использованием Создание запросов, возвращающих страницу данных БД SQL Server и Access позволяют использовать в запросах раздел ТОР, с помощью которого удается возвращать только записей. Так, следующий запрос возвращает первые 10 записей таблицы упорядочивая их по столбцам Country и SELECT TOP 10 CustomerlD, Country FROM ORDER BY Country, CustomerlD Если необходимо разбить результаты запроса на страницы, стоит воспользо ваться этим синтаксисом и вернуть только записи для конкретной страницы.

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

SELECT TOP 10 CustomerlD, ContactName, Country FROM Customers WHERE CustomerlD NOT IN (SELECT TOP 40 CustomerlD FROM Customers ORDER BY CustomerlD) ORDER BY Country, CustomerlD Здесь используется подзапрос, выбирающий первые 40 записей и упорядочи вающий их по столбцам Country и CustomerlD. Затем родительский запрос рает первые 10 записей, не входящие в набор подзапроса, и опять же упорядочивает их по столбцам Country и CustomerlD. Вот более универсальный синтаксис таких запросов:

SELECT TOP РаэмерСтраницы Поле1, Поле2, FROM Таблица WHERE ПолеКлюча NOT IN (SELECT TOP ПолеКлюча Таблица ORDER BY ORDER BY И БД SQL Server, и Access поддерживают разделы ТОР и NOT IN. Тем не указанные разделы поддерживаются далеко не всеми БД, и следовательно, их нельзя считать универсальным решением. Например, БД Oracle не поддерживают раздел ТОР.

БД Oracle предоставляют функцию, аналогичную по возможностям разделу ТОР Ч Oracle нумерует возвращаемые запросом записи, поэтому с по мощью функции rownum вы получите только первые п записей набора результа тов. Тем не менее Oracle генерирует номера записей до сортировки результатов запроса, а значит, чтобы с помощью функции rownum часть записей за проса, в котором определен порядок сортировки, нужно прибегнуть к маленькой хитрости. Немного обмана и пара подзапросов Ч и с помощью функции rownum вы вернете нужную часть записей запроса, использующего порядок сортировки, Так, следующий Oracle-запрос возвращает 41 Ч 50 записи таблицы Customers:

SELECT CustomerlD, CompanyName, ContactName, Country FROM (SELECT CustomerlD, CompanyName, ContactName, Country, rownum AS FROM (SELECT CustomerlD, CompanyName, ContactName, Country FROM Customers ORDER BY Country, CustomerlD) ГЛАВА 14 Создание эффективных Web-приложений WHERE <= 10) WHERE > Примечание Я не считаю себя знатоком СУБД Oracle. Возможно, есть более простой способ такие запросы для БД Oracle.

Приложение PagingOptions На прилагаемом к книге компакт-диске записано Web-приложение PagingOptions.

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

Редактирование данных на Web-странице Мы обсудили простой способ вывода данных на Web-странице. А как создать Web позволяющую редактировать представленные на ней данные?

Редактировать данные на Web-странице гораздо сложнее, чем на Windows форме. Помните: элементы управления Web-страницы, связанные с данными, ге на основе источника данных. Если вы выводите данные при помощи связанного элемента управления TextBox и пользователь изменяет его содержимое, это не отразится автоматически на содержимом источника данных.

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

Природа исключающая хранение сведений о состоянии, еще больше усложняет создание Web-страниц, позволяющих пользователю редакти ровать данные. Скажем, вы содержимое записи данных с помощью не скольких элементов управления TextBox. Как, после того как пользователь изме нит содержимое записи и щелкнет кнопку найти нужную запись и внести в нее соответствующие изменения? Кэшировали ли вы при помощи объекта где-нибудь данные, скажем, в объекте ViewState или Требуется ли вам об ратиться к БД, чтобы повторно выбрать нужную запись данных? Как изменились ли данные с момента первого обращения к ним пользователя?

Упрощение редактирования данных при помощи элемента управления DataGrid Не будем рвать с места в карьер. Для начала как с помощью элемента управления DataGrid реализовать на Web-странице позволяющие 552 Часть IV Создание эффективных приложений с использованием рать и редактировать данные. Наша цель Ч создать Web-страницу, аналогичную показанной на рис. 14-4.

a во of 00 | Submit ] Lumberjack Рис. 14-4. Пример Web-страницы, позволяющей редактировать данные Рис. Задание параметров редактирования данных в диалоговом окне Property Builder элемента управления DataGrid Точно так же, как DataGrid упрощает создание пользовательского интерфейса для постраничного представления этот элемент управления упро щает пользовательского интерфейса, позволяющего выбирать и редак тировать записи. Щелкните элемент управления DataGrid в среде Visual Studio правой кнопкой и выберите в контекстном меню команду Property Builder. Откро ГЛАВА 14 Создание эффективных Web-приложений ется диалоговое окно (рис. 14-5), в котором можно отобрать нужные столбцы и указать, имеет ли пользователь право редактировать их содержимое.

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

Обработка событий, связанных с редактированием содержимого DataGrid Как бы то ни было, задание свойств элемента управления DataGrid при помощи диалогового окна Property Builder в действительности не предоставляет пользо вателю возможности редактировать данные, с которыми связан DataGrid.

У элемента управления DataGrid на рис. 14-5 есть столбец Edit, Update, Cancel и столбец Delete. Если связать этот элемент управления с источником данных, Web страница выглядит аналогично показанной на рис, 14-6, of Order Рис. 14-6. Web-страница, использующая средства редактирования данных, предоставляемые элементом управления DataGrid пользователь щелкает кнопку Edit первого товара в корзине.

DataGrid не позволяет пользователю редактировать содержимое записи напрямую.

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

Затем, когда вы вызовете метод элемента управления DataGrid, DataGrid добавит в HTML-таблицу элементы управления TextBox, которые позволят пользо вателю изменить содержимое записи. Кроме того, DataGrid добавит для редакти руемой записи кнопки Update и Cancel.

554 Часть IV Создание эффективных приложений с использованием Передача изменений в БД После того как пользователь внесет нужные изменения и щелкнет кнопку событие элемента управления Его аргументы позволяют какую запись изменил пользователь, а также получить ее текущее содержимое. Тем не менее добавить логику для передачи измененного содержимого записи в БД вам придется вручную.

Внесение изменений в объект DataSet Чтобы внести изменения в объект DataSet найдите с помощью аргументов собы тия UpdateCommand соответствующий объект внесите изменения и за тем передайте их с помощью объекта Следующий фрагмент кода средствами события UpdateCommand получает название и новое ко личество товара, сведения о котором в корзине изменил пользователь. Затем код передает новые данные в БД с помощью объекта DataAdapter.

Visual Basic Private Sub source As Object, _ e As DataGridCommandEventArgs) Dim As New Dim tblCart As New Dim vueCart As New = "ProductName" Dim As String = Dim As Integer = Text) = End Sub Visual C# private void object source, DataGridCommandEventArgs e) I daCart = new DataTable tblCart = new vueCart = new DataView(tblCart);

vueCart.Sort = string strNewQuantity;

strNewQuantity = int intlndexToEdit;

intlndexToEdit = = I ГЛАВА 14 эффективных Web-приложений Создание собственных запросов UPDATE Показанный в предыдущем разделе фрагмент кода с помощью аргументов собы тия получает название и новое количество сведения о котором Б корзине изменил пользователь. Вместо того чтобы менять содержимое на основе этих данных и передавать коррективы БД с помощью объек та стоит воспользоваться этой же информацией и создать собствен ный запрос UPDATE, например:

UPDATE SET Quantity = WHERE = AND = Приложение ShoppingCart Ня прилагаемом к книге компакт-диске записано Web-приложение под названи ем ShoppingCart, позволяющее перемещаться по каталогу БД и добав лять товары из него в корзину. Кроме того, пользователь может изменять содер жимое корзины, добавляя товары или изменяя их количество. Приложение также позволяет разместить заказ и помещает его в таблицу Order Details БД Northwind.

Web-приложение включает две дополнительных Web-страницы, Database и предоставляющих одинаковую Единственное отличие этих страниц в том, где они хранят содержимое корзины пользователя. Как следует из названий, страница хранит со держимое корзины пользователя в БД, а страница StoreCartlnViewState Ч в объекте ViewState.

При создании системы приема заказов через Интернет я бы стал хранить со держимое корзин в БД, но мне подумалось, что полезно показать здесь и реше ние на основе объекта ViewState.

Вопросы, которые стоит задавать почаще Вопрос. Как, прежде чем я свяжу объект с элементом вернул ли запрос данные?

Ответ. Это Ч один из распространенных вопросов Web-разработчиков, не ющий, к простого решения. Если с помощью метода DataReader определить, вернул ли запрос записи, и затем связать с объектом DataReader элементы первая запись набора результатов запроса в них не отображается.

Если вы просто связываете объект DataReader с элементом DataGrid, с помощью свойства Count набора Items DataGrid удастся определить, сколько записей вернул запрос. Тем не менее иногда нужно узнать число возвращенных записей до того, как связать элементы управления с объектом DataReader.

Предположим, вы средствами следующего запроса выбираете заказы конкрет ного клиента:

SELECT FROM Orders = ?

При работе с БД, поддерживающей пакетные запросы, можно па кетный запрос, который сначала вернет число удовлетворяющий крите а затем вернет сами эти записи, например:

556 Часть IV Создание приложений с использованием SELECT FROM Orders WHERE CustomerlD = ?;

SELECT CustomerlD, Orders WHERE CustomerlD = ?

Visual Basic Dim strConn, As String strConn = & Dim As New strSQL "SELECT FROM Orders WHERE CustomerlD "SELECT CustomerlD, OrderDate Orders & "WHERE CustomerlD = Dim cmd As New 5) 5) = = "ALFKI" Dim rdr As = If > записи = Else вернул записей End If Visual C# strConn, strSQL;

strConn = + "Initial = new StrSQL = "SELECT FROM Orders WHERE CustomerlD = + "SELECT OrderlD, CustomerlD, OrderDate FROM Orders + "WHERE CustomerlD OleDbCommand cmd = new "ALFKI";

= "ALFKI";

cn.OpenO;

OleDbDataReader rdr = cmd.ExecuteReaderO;

if > 0) ГЛАВА Создание эффективных Web-приложений вернул записи = else < не } CloseC);

При работе с не поддерживающей пакетные запросы, следует воспользо ваться таким же способом, но выполнять не пакет, а отдельные запросы, Вопрос. Как использовать в Web-приложении оптимистическое управление кировками при передаче изменений в БД?

Ответ. На самом деле ответ зависит от требований вашего приложения. В при ложении ShoppingCart при передаче обновлений в БД используются только поля первичного ключа. Эта логика работает, потому что приложение поддерживает отдельную корзину для каждого сеанса. Таким образом, возможность редактиро вания содержимого одной корзины несколькими пользователями исключена, Для более жесткого контроля параллелизма можно непосредственно перед тем, как пользователь начнет редактировать ее содержимое. Тогда у вас будут оригинальные значения полей записи, которыми можно воспользо ваться в разделе WHERE запроса UPDATE и исключить возможность обновления записи, если ее уже успел изменить другой пользователь.

Более изящное решение Ч добавить в БД поле с типом данных и использовать в разделе WHERE значения первичного ключа и этого поля. При этом уменьшается объем данных, в объекте Session или в скры тых полях, Вопрос. У меня есть объект включающий два объекта между которыми определено отношение на основе объекта Как вывести в связанном элементе управления DataGrid только дочерние записи конкретной родительской записи?

Ответ. У объекта имеется метод возвращающий массив объектов который содержит только дочерние записи. Тем не менее свя зать элементы управления типа DataGrid с массивом объектов нельзя.

Можно создать объект DataView, инициализировать его, заполнив содержимым дочернего объекта и затем задать объекта DataView такое значение, при котором через DataView окажутся доступными только дочерние записи. К счастью, есть более простой способ.

Создайте объект DataView и инициализируйте его, заполнив содержимым ро дительского объекта DataTable. Затем найдите в объекте нужную роди тельскую запись и с помощью метода создайте объект содержащий только дочерние записи.

558 Часть IV Создание эффективных приложений с использованием Visual Basic Dim dsCustomersOrders As New Dim vueOrders As = New = Dim As = If intCustomerlndex >= 0 Then the desired parent row Dim As DataRowView = vueOrders = gridOrders.DataSource = vueOrders Else найти нужную запись End If Visual C# DataSet dsCustomersOrders = new DataView vueCustomers, vueOrders;

vueCustomers = new = "CustomerlD";

int intCustomerlndex = if 0) { the desired parent DataRowView drvCustomer = vueOrders = = vueOrders;

g ) I найти родительскую запись ПРИЛОЖЕНИЯ ПРИЛОЖЕНИЕ А Прочие поставщики данных подробно обсуждается работа с поставщиками данных отличающи мися от поставщика OLE DB Data Provider. В большинстве показанных ранее фрагментов кода, а также в книге в целом я взаимодействовал с БД при помощи поставщика OLE DB Data Provider. Конечно, и поставщи ки данных Первая версия Microsoft Framework включала SQL Client Data Provider. Вскоре после этого Microsoft выпустила поставщик ODBC Data Provider. Когда я работал над этой специалисты Microsoft раз рабатывали поставщик данных Oracle Client Data Provider.

Сейчас мы поговорим о том, как использовать эти поставщики данных и чем они отличаются от поставщика OLE DB Data Provider.

Поставщик данных SQL Client Data Provider Назначение поставщика SQL Client,NET Data Provider Ч обеспечить максимально быстрый доступ к БД Microsoft SQL Server и Microsoft Desktop Engine (MSDE).

Именованные параметры и маркеры параметров В отличие драйверов OLE DB и ODBC, использующих маркер параметров ?, по ставщик SQL Client Data Provider поддерживает именованные параметры.

Чтобы создать для поставщика SQL Client Data Provider параметризованный запрос, возвращающий заказы конкретного нужно использовать такой синтаксис:

SELECT ate Orders WHERE CustomeriD = Приложение А Прочие поставщики данных Поставщик OLE Data Provider использует позиционные маркеры па раметров и для него приведенный выше запрос будет выглядеть так:

ate FROM Orders WHERE = ?

Чем вызвана разница в синтаксисе? В целях помощи разработчикам, имеющим опыт работы предыдущими технологиями доступа к данным (OLE DB и ODBC), в поставщике OLE DB Data Provider предусмотрена поддержка универсальных маркеров параметров, использовавшиеся в этих технологиях для создания запросов.

OLE DB и ODBC разрабатывались как универсальные технологии доступа к дан ным. Их целью было создать независимый от СУБД код и заставить базовые ком поненты преобразовывать стандартный синтаксис в код, специфичный для БД.

Так почему же поставщик SQL Client Data Provider применяет именован ные параметры? Потому что того требует SQL Server. Выполнить в SQL Server па раметризованный запрос, включающий маркеры параметров нельзя.

Когда вы обращаетесь к БД SQL Server через поставщик OLE DB Data Provider с использованием параметризованного поставщик SQL Server OLE DB Provider анализирует запрос и заменяет маркеры параметров именованными па раметрами. Поставщик SQL Server OLE DB Provider поддерживает универсальный стандарт и преобразует запрос в формат, приемлемый для SQL Server. Назначе ние поставщика SQL Client Data Provider Ч максимально точно сопоставить свои функции аналогичным функциям SQL Server и обеспечить наибольшую про изводительность при работе с БД SQL Server. Таким SQL Client Data Provider не анализирует ваши запросы для преобразования маркеров параметров в именованные параметры.

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

Подключение к БД SQL Server с помощью объекта Для подключения к БД поставщик SQL Client Data Provider использует объект SqIConnection. Этот объект позволяет подключаться к БД SQL Server и За дать свойство объекта SqIConnection можно явно или при помо щи конструктора. Затем следует вызвать метод Visual Basic As String strConn = "Data Catalog=No & Dim As New 562 Приложения Visual C# string strConn;

strConn = "Data + = Вы, вероятно, заметили, что строка подключения здесь практически идентич на строке, использовавшейся для подключения к БД SQL Server и MSDE при помо щи объекта Единственное отличие в том, что опущен атрибут Подробнее об строки подключения, которые следует при менять при работе с объектом SqlConnection Ч в разделе документации посвященном свойству ConnectionString.

Объект SqlConnection также предоставляет два свойства, отсутствующих у объекта OleDbConnection: и Они доступны только для чтения, од нако их значения задают с помощью свойства ConnectionString объекта SqlConnection.

Получение результатов запроса с помощью объекта Для получения результатов запроса и помещения их в объект или применяют объект SqIDataAdapter. Работа с ним идентична использованию объекта но есть одно важное отличие. Как уже говорилось ранее, для выполнения параметризованного запроса следует использовать именованные параметры.

Visual Basic Dim strConn, As String strConn = "Data & strSQL = "SELECT FROM ders & _ "WHERE CustomerlD = Dim da As New strConn) Dim As = 5) = Dim As New Visual C# string strConn, strSQL;

strConn = "Data Catalog=N + strSQL = "SELECT CustomerlD, OrderDate FROM Or + "WHERE CustomerlD = SqIDataAdapter da = new SqlDataAdapter{strSQL, strConn);

SqlParameter param;

Приложение А Прочие поставщики данных = = DataTable = new Использование объектов и SqIDataReader Объект SqICommand позволяет выполнять командные запросы, а также получать результаты запросов при помощи объекта Visual Basic Dim As String strConn = Initial & strSQL = "SELECT CustomerlD, OrderDate FROM & _ "WHERE CustomerlD Dim As New Dim As New Dim param As param = bType.NChar, 5) = "ALFKI" Dim As SqIDataReader = Do While = & = & = & Loop Visual C# string strConn, strSQL;

strConn = "Data Catalog=N + StrSQL = "SELECT OrderlD, CustomerlD, OrderDate FROM ders + "WHERE CustomerlD SqlConnection = new SqICommand = new SqlParameter param;

param = bType.NChar, 5);

= "ALFKI";

SqIDataReader rdr = cmd. ExecuteReaderO;

while < = + = 564 Приложения + Примечание Объект не поддерживает значение Table из перечисления Как вы помните из главы объект также предоставляет объект Используя его, вы получите результаты запроса FOR XML с по мощью объекта Методы и пространство имен SqlTypes Как и объект объект SqlDataReader предоставляет ме тодов позволяющих возвращать значения с разными типами данных Кроме того, предоставляет и дополнительные методы соответствующие различным типам данных из пространства имен Следующий фрагмент получает данные одной из записей таблицы Orders и это содержимое с использованием типов данных, относящихся к про странству имен SqlTypes Ч SqlString и Visual Basic As String strConn = Initial & strSQL = "SELECT FROM Or ders & _ "WHERE OrderlD 10643" Dim As New SqlConnection(strConn) Dim As New Dim As SqlDataReader Dim As Dim As Dim As SqlDateTime = Row) If Then intOrderlD = StrCustomerlD = = End If Приложение А Прочие поставщики данных Visual string strConn = "Data + strSQL = "SELECT OrderDate Or " + "WHERE OrderlD = 10643";

= new = new SqlStrlng = Row);

if { intOrderlD = strCustomerlD datOrderDate = } Зачем использовать эти специфичные для типы данных? Основ ных причин две.

Х Производительность. Типы данных из пространства имен SqlTypes повыша ют производительность кода, поскольку именно их внутренне использует по ставщик SQL Data Provider. Если возвращать данные с использова нием типов данных вызывая методы и т.д., SQL Client Data Provider придется преобразовывать эти данные. Исполь зование специфичных для поставщика типов данных позволяет избежать та кого преобразования. Я обнаружил, что производительность кода на основе пространства имен SqlTypes обычно на выше производительности стандартные типы данных Х Простота кода. Мало кому из программистов нравится обрабатывать значе ния NULL. В показанном ранее фрагменте кода, использовавшем стандартные типы данных нет проверок на наличие значений NULL. Если бы любое из полей в запросе содержало значение NULL, код сгенерировал бы исключение.

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

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

При применении типов обращение к объекту SqlDataReader 566 Приложения ется, т. е. результаты запросов удается получить быстрее. Кроме того, это по зволяет быстрее закрыть объект SqlDataReader и освободить соединение, Вызов хранимых процедур Объект SqlCommand позволяет вызывать хранимые процедуры SQL и Данный объект предоставляет свойство CommandType, упрощающее код для вы зова хранимый процедур. Можно задать свойству объекта SqlCommand имя хранимой процедуры, свойству CommandType Ч значение и затем вызвать эту хранимую процедуру:

Visual Basic Dim As String = "Data Initial Dim As New Dim cmd As New = Dim param As = bType.NChar, 5) = Dim rdr As SqlDataReader = Do While Loop Visual C# string strConn;

strConn = "Data Catalog=N + = new SqlCommand cmd new = SqlParameter param;

param = SqlD bType.NChar, 5);

= SqlDataReader = while (rdr.ReadO) Воспользовавшись средствами трассировки SQL Server, вы увидите, что постав щик SQL Client Data Provider преобразовал эту информацию в следующий синтаксис:

Приложение А Прочие поставщики данных EXEC Для обеспечения максимальной производительности кода используйте такой же синтаксис, но не изменяйте значение свойства no умолчанию Ч Text. Чтобы получить с использованием этого синтаксиса данные при помощи параметров вывода, добавьте после имени параметра ключевое OUT;

EXEC OUT Получение информации схемы БД Объект предоставляет метод позволяющий получить из БД информацию схемы, например список таблиц или столбцов. Пря мого этой функции у SQL Client Data Provider нет.

Тем не менее SQL Server позволяет получить такие данные с помощью пред ставлений информационной схемы. Следующий запрос возвращает сведения о таблицах БД SQL Server:

SELECT * FROM Для получения сведений о таблицах, хранимых процедурах, ограни и т.д. предусмотрены различные представления. Чтобы вам было от чего отталкиваться, я покажу некоторые наиболее распространенные запросы.

Данный запрос возвращает список имен таблиц:

SELECT TABLE_NAME FROH = Данный запрос возвращает список имен представлений:

SELECT FROM WHERE = Данный запрос возвращает список имен столбцов таблиц:

SELECT NUMERIC_PRECISrON, FROH WHERE TABLE_NAME IN (SELECT FROM WHERE = ORDER BY Данный запрос возвращает список имен SELECT FROM WHERE = Данный запрос возвращает список параметров этих процедур:

SELECT NUMERIC_PRECISION, NUMERIC_SCALE FROH WHERE IN (SELECT SPECIFIC_NAME FROM INFORMATION Приложения WHERE TYPE = ORDER BY SPECIFIC NAME Подробнее об использовании представлений информационной схемы Ч в SQL Server Books Online.

Поставщик данных ODBC Data Provider Вскоре после выхода Framework выпустила третий поставщик данных Ч ODBC Data Provider. Он для взаимодействия с БД, ис пользующими ODBC-драйверы.

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