|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Глава 16Страницы ASP.NET Для новичков в мире C# и .NET может показаться странным, почему в книгу включена глава, посвященная ASP.NET. Это совершенно новый язык, не так ли? Не совсем. Фактически, как мы увидим, можно использовать C# для создания страниц ASP.NET. Но мы забегаем вперед. Прежде всего необходимо обсудить, что же такое ASP.NET. ASP.NET (Active Server Pages.NET — активные серверные страницы .NET), поставляется как часть платформы .NET и является технологией, которая позволяет динамически создавать документы на сервере Web, когда они запрашиваются через HTTP. Это в основном документы HTML, хотя в равной степени можно создавать, например, документы WML для использования в браузерах WAP или на самом деле что-то еще с помощью типа MIME. Технология ASP.NET аналогична таким технологиям, как PHP, ColdFusion и другим, но между ними имеется одно существенное различие. ASP.NET, как предполагает ее название, была создана с целью полной интеграции в платформу .NET, часть которой включает поддержку C#. Вполне возможно, что читатель обладает опытом работы с последней технологией компании Microsoft для получения динамической генерации содержимого — ASP. В этом случае, он должен, вероятно, знать, что программирование в этой технологии использует язык сценариев, такой как VBScript или JScript. Это работало, но некоторые вещи были затруднительны для тех программистов, которые привыкли использовать 'правильные' языки программирования, что приводило в результате к потере производительности. Одним из основных отличий, связанных с использованием более развитых языков программирования, является обеспечение полной серверной объектной модели для использования во время работы. ASP.NET предоставляет доступ ко всем элементам управления на странице, как к объектам в обширном окружении. Также на стороне сервера мы имеем доступ ко всем другим требуемым классам .NET, позволяя интеграцию многих полезных служб. Элементы управления, используемые на странице, функциональны, фактически можно делать все то же, что и с классами форм в Windows, что дает большую гибкость. По этой причине страницы ASP.NET, создающие содержимое HTML, часто называют формами Web. В этой главе мы проведем более подробное рассмотрение ASP.NET, включая то, как она работает, что мы можем с ней делать и где используется C#. Введение в ASP.NETASP.NET пользуется Информационным сервером Интернета (Internet Information Server, IIS) для доставки содержимого в ответ на запросы HTTP. Страницы ASP.NET находятся в файлах с расширением .aspx, и базовая архитектура выглядит следующим образом: Во время обработки ASP .NET мы имеем доступ ко всем классам .NET, специальным компонентам, созданным на C# или других языках, базам данных и т.д. Фактически мы обладаем такими же возможностями, как при выполнении приложения C#, т.е. использование C# в ASP.NET заключается в эффективном выполнении приложения C#. Файл ASP.NET может содержать любые элементы из следующего списка: □ Обработка инструкций для сервера □ Код на C#, VB.NET, JScript.NET или любом другом языке, который поддерживает платформа .NET сейчас или может поддерживать в будущем □ Содержимое в любой ферме, подходящей для сгенерированного ресурса, такого как HTML □ Встроенные серверные элементы правления ASP.NET Поэтому, фактически, можно иметь файл ASP.NET, состоящий просто из Hello! без какого-либо дополнительного кода или инструкций вообще. Это приводит просто к созданию возвращаемой страницы HTML (так как HTML является используемым по умолчанию выводом страниц ASP.NET), содержащей именно этот текст. Как мы увидим позже в этой главе, можно также разделить отдельные части кода по различным файлам, что создаст более логичную структуру. Управление состоянием в ASP.NETОдно из ключевых свойств страниц ASP.NET состоит в том, что они не обладают состоянием. По умолчанию никакая информация не сохраняется на сервере между запросами пользователя (хотя существуют методы, которые позволяют при желании это делать). На первый взгляд это кажется немного странным, так как управление состоянием кажется существенным для интерактивных сеансов, удобных для пользователя. Однако ASP.NET предоставляет достаточно удобный способ обхода этой проблемы, чтобы сделать управление сеансом почти прозрачным. По сути, информация, например, о состоянии элементов управления в форме Web (данные, введенные в текстовые поля, выбор из выпадающих списков и т.д.) хранится в скрытых полях представления состояния (viewstate), которые являются частью страницы, сгенерированной сервером и переданной пользователю. Если в дальнейшем требуется серверная обработка типа пересылки данных формы, то происходит возврат (postback) этой информации на сервер. На сервере эта информация используется для повторного заполнения объектной модели страницы, позволяя нам действовать на ней, как если бы изменения были бы сделаны локально. Мы скоро увидим это в действии и укажем особенности. Формы Web ASP.NETКак упоминалось ранее, большая часть функциональности ASP.NET достигается с помощью форм Web. Скоро мы перейдем к этому вплотную и создадим простую форму Web, чтобы получить начальную точку для исследования этой технологии. Прежде всего, однако, мы должны взглянуть на особенности, имеющие отношение к созданию форм Web. Необходимо отметить, что многие разработчики ASP.NET используют для создания файлов просто текстовый редактор, такой как notepad. Дело облегчается тем, что можно, как отмечалось ранее, объединить весь код в одном файле. Код заключается между тегами <script>с использованием двух атрибутов в открывающем теге <script>следующим образом: <script language="с#" runat="server"> // Серверный код располагается здесь </script> Атрибут runat="server"здесь является критически важным (и мы увидим его неоднократно в этой главе), так как он дает указание IIS выполнить этот код на сервере, а не посылать его клиенту, предоставляя, тем самым, доступ к богатому окружению, рассмотренному ранее. Можно поместить наши функции, обработчики событий и т.д. в серверные блоки сценариев. Если опустить атрибут runat="server", мы, по сути, предоставим клиентский код, который откажет, если он использует какое-либо кодирование в серверном стиле, которое мы увидим в этой главе. Однако могут возникать ситуации, когда понадобиться предоставить клиентский код (на самом деле ASP.NET сам иногда создает некий код в зависимости от возможностей браузера и используемого кода формы Web). К сожалению, мы не можем использовать здесь C#, так как это будет требовать платформы .NET на стороне клиента, что может не всегда существовать, поэтому JScript является, вероятно, лучшей возможностью (так как он поддерживается на большом множестве клиентских браузеров). Чтобы изменить язык, мы просто изменяем значение атрибута languageследующим образом: <script language="jscript"> // Клиентский код расположен здесь, можно также использовать vbscript. </script> В равной степени можно создавать файлы ASP.NET в Visual Studio, что прекрасно для нас подходит, так как мы уже знакомы с этой средой для программирования C#. Однако применяемая по умолчанию настройка проекта для приложений Web в этой среде предоставляет чуть более сложную структуру, чем один файл .aspx. Но это не является для нас проблемой, так как делает вещи более логичными (более подходящими для программиста и менее для разработчика Web). На основе этого в данной главе мы будем пользоваться Visual Studio.NET для программирования ASP.NET. Рассмотрим пример. Создайте новый проект типа C# Type Web Application, как показано ниже: По умолчанию VS будет использовать расширения FrontPage для настройки приложения Web в требуемом месте, которое может быть удаленным, если сервер Web находится на другой машине. Но и для этого существует альтернативный (и более быстрый) метод, использование файловой системы через LAN (что является, конечно, невозможным, если удаленный сервер Web находится не в той же LAN, что и сервер разработки). Если первый метод отказывает, то VS будет пробовать другой. Независимо от используемого метода, VS поддерживает локальный кэш всех файлов проекта, причем в синхронизации с файлами на сервере Web. Через какое-то время Visual Studio должна создать следующее: □ Новое решение, PCSWebAppl, содержащее приложение Web на C# с именем PCSWebAppl □ AssemblyInfo.cs— стандартный код для описания сборки □ Global.asax— глобальная информация и события приложения (будет показано позже в этой главе) □ PCSWebAppl.disco— файл, описывающий все службы Web в проекте, дающий возможность динамического обнаружения (подробности в следующей главе) □ Web.config— конфигурационная информация для приложения (будет показано позже в этой главе) □ WebForm1.aspx— первая страница ASP NET в приложении Web Мы покажем все сгенерированные файлы в ходе изложения этой и следующих двух глав, в данный момент нам необходимо сосредоточиться на сердцевине приложения — созданном файле .aspx. Файлы .aspxможно просматривать двумя способами: в виде модели и в виде кода. Это аналогично тому, что используется для форм Windows, как мы видели раньше в этой книге. Начальное представление в VS является модельным представлением: Текст, показанный здесь по умолчанию, не является текстом, который мы увидим в приложении, это просто примечание от VS, сообщающее, какой режим компоновки выбран. Здесь используется режим GridLayout, который допускает большую гибкость в управлении позиционированием, но можно сменить его на FlowLayout, если требуется более традиционная схема позиционирования типа HTML. Мы рассмотрим это немного позже. Если выбрать представление HTML с помощью кнопки внизу окна компоновки, мы увидим код, созданный внутри файла .aspx: <%@ Page language="#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebAppl.WebForm1" %> <html> <head> <meta name=vs_targetSchema content="Internet Explorer 5.0"> <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> </head> <body MS_POSITIONING="GridLayout"> <form method="post" runat="server"> </form> </body> </html> Здесь элемент <html>заполнен несколькими метаданными, которые нас не касаются, и элементом <form>для размещения кода ASP.NET. Наиболее важной вещью в этом элементе является атрибут runat. Точно так же, как в блоках серверного кода, которые мы видели в начале раздела, он задан как server, и значит, обработка формы будет иметь место на сервере. Если не включить этот атрибут, то никакой серверной обработки выполняться не будет, и форма не будет ничего делать. Другая интересная вещь в отношении этого кода состоит в теге <@% Page %>в начале файла. Этот тег определяет характеристики страницы, которые важны для нас как разработчиков приложения Web на C#. Прежде всего здесь существует атрибут language, который определяет, что на этой странице будет использоваться C#, как мы видели раньше в блоках <script>(значение по умолчанию для приложения Web является VB.NET, хотя это может быть изменено через конфигурацию IIS). Следующие три атрибута являются необходимыми, так как код, управляющий страницей, был задан VS для размещения в отдельном файле WebForm1.aspx.cs. Этот файл, который мы сейчас рассмотрим, содержит определение класса, используемого в качестве базового для страницы форм Web. (Теперь мы начинаем видеть, как ASP.NET соединяется с насыщенной объектной моделью). В этом файле для создания HTML базовый класс будет использоваться в соединении с кодом.
Так как мы предоставляем специальный базовый класс для страницы, мы будем иметь специальные события. Чтобы гарантировать, что IIS знает об этом, мы используем атрибут AutoEventWireup, который означает, что обработчик событий Page_Load(), вызываемый при загрузке страницы, связывается автоматически с событием OnPageLoad. Задавая этот атрибут как false, мы должны предоставить, если потребуется, свой собственный код для выполнения этого, что даст нам большую свободу действий. Теперь посмотрим на "код позади" кода, сгенерированного для этого файла. Чтобы сделать это, щелкнем правой кнопкой мыши на WebForm1.aspxв утилите анализа решения (solution explorer) и выберем View Code. Код WebForm1.aspx.csдолжен загрузиться в текстовый редактор. Прежде всего можно видеть объявление пространств имен для приложения Web, за которым следует используемое по умолчанию множество ссылок, требуемое для базового использования: namespace PCSWebAppl { using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlCotrols; Двигаясь дальше, мы видим определение WebForm1— базового класса, используемого для страницы .aspx. Этот класс наследует из System.Web.UI.Page, базового класса форм Web: /// <summary> /// Краткое описание WebForm1 /// </summary> public class WebForm1 : System.Web.UI.Page { Остальная часть кода в этой форме выполняет различные инициализационные задачи, а также включает код, требуемый для создания форм Web в VS. Конструктор регистрирует обработчика событий Page_Init(), вызываемого во время активации страницы и используемого VS для кода, связанного с созданием временных добавлений к форме (хранимых в InitializeComponent(), который вызывается этим обработчиком). Существует также обработчик событий Page_Load(), упоминавшийся ранее: public WebForm1() { Page.Init += new System.EventHandler(Page_Init) } protected void Page_Load(object sender, System.EventArgs e) { // Поместите здесь код пользователя для инициализации страницы } protected void Page_Init(object sender, EventArgs e) { // // CODEGEN: Этот вызов требуется для ASP.NET Windows // Form Designer. // InitializeComponent(); } Сам метод InitializeComponent()содержится в блоке #region, поэтому мы используем схематичное представление в VS, чтобы его скрыть, поскольку он быстро заполнится сгенерированным кодом VS (так же как аналогичный ему метод в коде форм Windows): #region код, созданный Web Form Designer /// <summary> /// Метод, требуемый для поддержки Designer; не изменяйте /// содержимое этого метода с помощью редактора кода. /// </summary> private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } } Так как AutoEventWireupбыл задан как false, то InitializeComponent()должен зарегистрировать Page_Load()с событием Load. Строго говоря, этот код больше, чем требуется для простой страницы формы Web ASP.NET, которую мы уже видели (хотя и в качестве тривиального примера). Однако созданная структура приспособлена для целей повторного использования и расширения с помощью технологий C#, не требуя заметного объема накладных расходов, поэтому мы будем ее использовать. Серверные элементы управления ASP.NETСозданный нами код делает пока еще очень немногое, поэтому далее нам нужно добавить некоторое содержимое. Можно сделать это в VS, используя построитель форм Web, который поддерживает режим перетаскивания элементов и добавления через код точно так же, как построитель форм Windows. Существует четыре типа элементов управления, которые можно добавлять к страницам ASP.NET: □ Элементы управления сервера HTML — элементы управления, которые имитируют элементы HTML, известные разработчикам HTML □ Элементы управления сервера Web — новое множество элементов управления, некоторые из которых имеют такую же функциональность, как и элементы управления HTML, но с общей схемой имен свойств и т.д., служащей для облегчения разработки (и обеспечения согласованности с аналогичными элементами управления форм Windows); существует также несколько совершенно новых и очень мощных элементов управления, как мы увидим позже □ Элементы управления проверкой достоверности — множество элементов управления, способных выполнять простую проверку ввода пользователя □ Заказные и обычные элементы управления пользователя — элементы управления, используемые разработчиком, которые можно определить рядом способов, как будет показано в главе 18
Давайте добавим пару элементов управления сервера Web в наш проект. Все элементы управления сервера Web и проверкой достоверности даются в следующей форме типа элемента XML: <asp:X runat="server" attribute="value">Contents</asp:X> Здесь Label(используемый для вывода простого текста), где текст можно определить любым образом. Другие элементы управления могут использовать схему вложенности элементов для определения их иерархии, например, Table(определяющий таблицу), который может содержать элементы TableRow, чтобы декларативно определить строки таблицы. Отметим, что, так как синтаксис элементов управления основывается на XML (хотя они могут использоваться встроенными в код, не являющийся XML, такой как HTML, будет ошибкой отсутствие закрывающих тегов, отсутствие />для пустых элементов или перекрытие элементов управления. Наконец, мы снова видим атрибут runat="server"в элементе управления сервера Web. Он важен здесь так же, как и в других местах, и распространенной ошибкой является пропуск этого атрибута, приводящий к неработающим формам Web. В первом примере мы делаем все простым способом. Измените представление в виде кода HTML для WebForm1.aspxтак: <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebAppl.WebForm1" %> <html> <head> <meta name=vs_targetSchema content="Internet Explorer 5.0" > <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> </head> <body MS_POSITIONING="GridLayout"> <form method="post" runat="server"> <asp:Label Runat="server" ID="resultLabel"/> <br> <asp:Button Runat="server" ID="triggerButton" Text="Click Me" /> </form> </body> </html>
Здесь мы добавили два элемента управления формы Web — метку и кнопку. Возвращаясь к окну построения, можно увидеть, что были добавлены эти элементы управления, именованные с помощью своих атрибутов ID. Как и для форм Windows, мы имеем полный доступ к свойствам, событиям и т.д. через окно Properties, и можем видеть немедленные изменения в коде или визуальном представлении, когда производятся эти изменения. Теперь посмотрим еще раз на WebForm1.aspx.cs. Мы видим, что к классу WebForm1были добавлены следующие два члена: protected System.Web.UI.WebControls.Button triggerButton; protected System.Web.UI.WebControls.Label resultLabel; Любые добавляемые серверные элементы управления автоматически будут становиться частью объектной модели создаваемой формы. Чтобы приложение действительно что-то делало, давайте добавим обработчик событий для нажатия кнопки. Здесь мы можем либо ввести имя метода в окне Properties для кнопки, либо просто сделать двойной щелчок на кнопке, чтобы получить используемый по умолчанию обработчик событий. Если сделать двойной щелчок на кнопке, то автоматически добавится следующий метод обработки события: protectеd void triggerButton_Click(object sender, System EventArgs e) { } Он соединяется с кнопкой с помощью кода, добавляемого в InitializeComponent(): private void InitilizeComponent() this.triggerButton.Click += new System.EventHandler(this.triggerButton_Click); this.Load += new System.EventHandler(this.Page_Load); } Изменим код в triggerButton_Click()следующим образом: protected void triggerButtor_Click(object sender, System.EventArgs e) { resultLabel.Text = "Button clicked:"; } Теперь мы готовы к запуску приложения. Строим приложение в VS обычным образом, все файлы будут откомпилированы и/или помещены на сервер Web готовыми к использованию. Чтобы протестировать приложение Web, можно либо запустить приложение (что предоставит весь набор средств отладки VS), либо просто направить браузер на адрес http://localhost/PCSWebAppl/webForm1.aspx. В любом случае можно будет увидеть кнопку Click Me на странице Web. До нажатия кнопки посмотрим на код, полученный браузером, с помощью View|Source (в IE). Раздел <form>должен выглядеть примерно следующим образом: <form name="ctrl1" method="post" action="webform1.aspx" id="ctrl1"> <input type="hidden" name="_VIEWSTATE" value="dDwtMzQ3NzI5OTM4Ozs+0RD39htoKLKMO7Yf41cFXM2GjQU=" /> <span id="resultLabel"></span> <br> <input type= "submit" name="triggerButton" value="Click Me" id="triggerButton" /> </form> Элементы управления сервера Web сгенерировали правильный код HTML — <span>и <input>для <asp:Label>и <asp:Button>, соответственно. Также существует поле <input type="hidden">с именем _VIEWSTATE. Оно инкапсулирует состояние формы, как это упоминалось ранее. Эта информация используется, когда форма посылается назад на сервер для воссоздания UI, отслеживания изменений и т.д. Отметим, что для этого был сконфигурирован элемент <form>, он будет отправлять данные назад в WebForm1.aspx(определенное в action) с помощью операции HTTP POST (определенной в method). Ему было также присвоено имя ctrl1. Если посмотреть на HTML, сгенерированный более сложными формами Web, то можно увидеть, что это обычный тип присваивания и он соответствует способу, которым работает ASP.NET. После нажатия на кнопку и появления текста проверьте снова исходный код HTML (пробелы добавлены для ясности) <form name="ctrl1" method="post" асtion="WebForm1.aspx" id="ctrl1"> <input type="hidden" name="_VIEWSTATE" value="dDwtMzQ3NzI5OTM4O3Q802w8MTwxPjs+ O2wbdDw7bDwxPDE+Oz47bDx0 PHA8cDxsPFR1eHQ7PjtsPEJ1dHRvbiBjbGlj a2VkITs+Pjs+Ozs+Oz4+Oz4+Oz6TChBE9Yvrgb7dL38o2VsGzc/RgA==" /> <span id="resultLabel">Button clicked</span> <br> <input type="submit" name="triggerButton" value="Click Me" id="triggerButton" /> </form> В этот раз значение VIEWSTATEсодержит больше информации, так как результат HTML опирается не только на используемый по умолчанию вывод страницы ASP NET. В сложных формах это может быть на самом деле очень длинная строка, но мы не должны выражать недовольства, так как очень много было сделано для нас "за сценой". Палитра элементов управленияВ этом разделе мы кратко рассмотрим доступные элементы управления, прежде чем соберем их вместе в большом и более интересном приложении. Этот раздел поделен на элементы управления сервера Web и элементы управления проверкой достоверности. Обратите внимание, что в описаниях элементов упрaвлeния ссылка идет на "свойства", во всех случаях соответствующий атрибут для использования в коде ASP.NET называется идентично. Здесь представлены только наиболее часто используемые свойства. Элементы управления сервера WebВсе элементы управления сервера Web наследуются из класса System.Web.UI.WebControls.WebControl, который, в свою очередь, наследуется из класса System.Web.UI.Control. В связи с этим они обладают многими общими свойствами и событиями, которые при необходимости можно использовать. Их достаточно много, поэтому не все они будут здесь показано, также как и свойства, и события самих элементов управления сервера Web. Многие из часто используемых унаследованных свойств имеют дело со стилем вывода изображения с помощью таких свойств, как ForeColor, Backcolor, Fontи т. д. Но можно прибегнуть также к помощи классов CSS (каскадных таблиц стилей), задавая для строкового свойства CssClassимя класса CSS в отдельном файле. Другими примечательными свойствами являются Widthи Heightдля размера элемента управления, AccessKeyи TabIndexдля облегчения взаимодействия пользователя, и Enabledдля определения того, что функциональность элемента управления обеспечивается в форме Web. Из событий мы, наверно, чаще всего будем использовать унаследованное событие Eventдля выполнения инициализации элемента управления, и PreRenderдля выполнения последних модификаций перед тем, как HTML выведет элемент управления. Существует множество других событий и свойств, многие из них мы обсудим более подробно позже, при рассмотрении специальных элементов управления. Список элементов управления сервера Web включает в себя:
Элементы управления проверкой достоверности предоставляют метод проверки достоверности ввода пользователя (в большинстве случаев) вообще без написания какого-либо кода. Когда инициируется обратная отправка, каждый элемент управления выполняет проверку, которую он подтверждает, и изменяет соответственно свое свойство isValid. Если это свойство будет false, то ввод пользователя для элемента проверки достоверности не получил подтверждение. Страница, содержащая все элементы управления, также имеет свойство isValid: если у какого-либо из элементов управления проверкой достоверности свойство isValidзадано как false, то это свойство страницы также будет иметь значение false. Это свойство можно проверять из серверного кода и действовать в соответствии с ним. Однако элементы управления проверкой достоверности имеют вторую функцию. Они не только проверяют элементы управления во время выполнения, но могут также автоматически выводить пользователям полезные рекомендации. Если задать для свойства ErrorMessageкакое угодно текстовое значение, то пользователь увидит его, когда попытается отправить назад неверные данные. Хранящийся в ErrorMessageтекст можно вывести в том месте, где расположен элемент управления проверкой достоверности или в другом месте с сообщениями обо всех других элементах управления проверкой достоверности на странице. Такое поведение достигается с помощью элемента управления ValidationSummary, который выводит, если потребуется, все сообщения об ошибках вместе с дополнительным текстом. В браузерах, которые это поддерживают, данные элементы управления создают клиентские функции JavaScript, чтобы упростить свое поведение по проверке. Это означает, что в некоторых случат обратная пересылка даже не происходит, так как элементы управления проверкой могут в некоторых ситуациях воспрепятствовать этому и вывести сообщения об ошибках без участия сервера. Все элементы управления проверкой наследуют из класса BaseValidator, и поэтому обладают некоторыми общими важными свойствами. Возможно, наиболее важным является рассмотренное выше свойство ErrorMessageи в этом случае свойство ControlToValidateможно считать вторым по важности. Это свойство определяет идентификатор (ID) элемента управления, который проверяется. Другим важным свойством является Display, которое определяет, поместить ли текстовое сообщение в итоговой позиции проверки (если задано как none) или в позиции проверяющего элемента. Имеется также возможность оставить место для сообщения об ошибке, даже когда оно не выводится (задавая Displayкак Static) или динамически выделять место, когда потребуется, что может слегка сдвигать содержимое страницы (задавая Displayкак Dynamic). Мы скоро рассмотрим пример, но сначала кратко опишем различные элементы управления проверкой:
Пример серверного элемента управленияТеперь мы разберем простой пример — пришло время взглянуть на более сложный сценарий. Давайте создадим каркас приложения Web — утилиту для заказа помещения для собраний. В данный момент он будет содержать только внешний интерфейс и простую обработку событий, позже мы расширим его с помощью ADO.NET и связывания данных, чтобы включить серверную бизнес-логику. Форма Web, которую мы собираемся создать, будет содержать поля для имени пользователя, имени события, помещения для собраний и служителей вместе с календарем для выбора даты (предполагается в этом примере, что события продолжаются в течение полного дня). Используем элементы управления проверкой для всех полей, за исключением календаря, который будет проверяться на сервере и предоставлять дату по умолчанию в случае, если ничего не было введено. Для тестирования интерфейса пользователя мы будем также иметь на форме элемент управления Label, который можно использовать для вывода представляемых результатов. Вначале создадим в Visual Studio новый проект приложения Web с именем PCSWebApp2. Затем мы создаем форму, которая генерируется с помощью следующего кода в WebForm1.aspx(код, генерируемый автоматически, не выделен): <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="PCSWebApp2.WebForm1" %> <html> <head> <meta content=False name=vs_showGrid> <meta content="Internet Explorer 5.0" name="vs_targetSchema> <meta content="Microsoft Visual Studio 7.0" name=GENERATOR> <meta content=C# name=CODE_LANGUAGE> </head> <body> <form method="post" runat="server"> <h1 align="center">Enter details and set a day to initiate an event.</h1> <br> <table borderColor="#000000" cellSpacing="0" cellPadding="8" rules="none" align="center" bgColor="#fff99e" border="2" width="540"> <tr> <td vAlign="top">Your Name:</td> <td vAlign="top"> <asp:textbox id="nameBox" runat="server" width="160px" /> <asp:requiredfieldvalidator id=validateName Runat="server" errormessage="You must enter a name." ControlToValidate="nameBox" display="None" /> </td> <td vAlign="center" rowSpan="4" > <asp:calendar id="calendar" runat="server" BackColor="White" /> </td> </tr> <tr> <td vAlign="top">Event Name:</td> <td vAlign="top"> <asp:textbox id="eventBox" runat="server" width="160px" /> <asp:requiredfieldvalidator id="validateEvent" Runat="server" errormessage="You must enter an event name." ControlToValidate="eventBox" display="None" /> </td> </tr> <tr> <td vAlign="top">Meeting Room:</td> <td vAlign="top"> <asp:dropdownlistid="roomList" runat="server" width="160px"> <asp:ListItem Value="1">The Happy Room</asp:ListItem> <asp:ListItem Value="2">The Angry Room</asp:ListItem> <asp:ListItem Value="3">The Depressing Room</aspListItem> <asp:ListItem Value="4">The Funked Out Room</asp:ListItem> </asp:dropdownlist> <asp:requiredfieldvalidator id="validateRoom" Runat="server" errormessage="You must select a room." ControlToValidate="roomList" display="None" /> </td> </tr> <tr> <td vAlign= " top">Attendees: </td> <td vAlign="top"> <asp:listbox id="attendeeList" runat="server" width="60px" selectionmode="Multiple" rows="6"> <asp:ListItem Value="1">Bill Gates</asp:ListItem> <asp:ListItem Value="2">Monika Lewinsky</asp:ListItem> <asp:ListItem Value="3">Vincent Price</asp:ListItem> <asp:ListItem Value="4">Vlad the Impaler</asp:ListItem> <asp:ListItem Value="5">Iggy Pop</asp:ListItem> <asp:Listltem Value="6">William Shakespeare</asp:ListItem> </asp:listbox> <asp:requiredfieldvalidator id="validateAttendees" Runat="server" errormessage="You must have at least one attendee." ControlToValidate="attendeeList" display="None" /> </td> </tr> <tr> <td align="middle" colSpan="3"> <asp:button id="submitButton" runat="server" width="100%" Text="Submit meeting room request" /> </td> </tr> <tr> <td align="middle" colSpan="3"> <asp:validationsummary id="validationSummary" Runat="server" headertext="Before submitting your request:" /> </td> </tr> </table> <br> Results: <asp:Label Runat="server" ID="resultLabel" Text="None." /> </form> </body> </html> После заголовка страницы, который записан между тегами HTML <h1>, чтобы сделать его крупным текстом в стиле заголовка, основное тело формы помещается между тегами HTML <table>. Мы могли бы использовать управляющий элемент таблицы сервера Web, но это внесло бы ненужную сложность, так как таблица используется только для форматирования вывода, а не как динамический элемент интерфейса пользователя. Таблица делится на три столбца, первый из которых содержит простые текстовые метки, второй содержит поля интерфейса пользователя, соответствующие текстовым меткам (вместе с элементами управления проверкой для них), и третий, содержащий элемент управления календарем для выбора даты, которая размещается на четырех строках. Пятая строка содержит кнопку отправки, охватывающую все столбцы, и шестая строка содержит элемент управления validationSummaryдля вывода сообщений об ошибках, когда потребуется (все остальные элементы управления проверкой имеют атрибут display="none", так как они будут использовать для вывода это итоговое поле). Под таблицей находится простая метка, которую можно использовать в настоящее время для вывода результатов, пока не будет добавлен доступ к базе данных. Большая часть кода ASP.NET в этом файле удивительно проста. Специального замечания требует способ, которым элементы списка присоединяются к элементам управления для выбора помещения для встречи, а также нескольких лекторов: <asp:dropdownlist id="roomList" runat="server" width="160px"> <asp:ListItem Value="1">The Happy Room</asp:ListItem> <asp:ListItem Value="2">The Angry Room</asp:ListItem> <asp:ListItem Value="3">The Depressing Room</asp:ListItem> <asp:ListItem Value="4">The Funked Out Room</asp:ListItem> </asp:dropdownlist> ... <asp:listbox id="attendeeList" runat="server" width="160px" selectionmode="Multiple" rows="6"> <asp:ListItem Value="1">Bill Gates</asp:ListItem> <asp:ListItem Value="2">Monika Lewinsky</asp:ListItem> <asp:ListItem Value="3">Vincent Price</asp:ListItem> <asp:ListItem Value="4">Vlad the Impaler</asp:ListItem> <asp:ListItem Value="5">Iggy Pop</asp:ListItem> <asp:ListItem Value="6">William Shakespeare</asp:ListItem> </asp:listbox> Здесь объекты ListItemсоединяются с двумя элементами управления сервера Web. Эти объекты не являются полноценными элементами управления сервера Web, в связи с чем нам не нужно использовать на них runat="server". Когда страница обрабатывается, то записи <asp:ListItem>используются для создания объектов ListItem, которые добавляются к коллекции Itemsродительского элемента управления списком. Это облегчает нам инициализацию списков, так как не требуется писать код для этого самостоятельно (мы должны были бы создать объект ListItemCollection, добавить объекты ListItemи затем передать коллекцию элементу управления списком). Конечно, мы можем по-прежнему сделать все это программным путем, если понадобиться. Созданная форма выглядит следующим образом: Это полностью функциональный интерфейс пользователя, который поддерживает свое собственное состояние между запросами сервера и проверяет ввод пользователей. Учитывая краткость приведенного выше кода, это больше чем достаточно. Фактически, нам остается сделать еще совсем немного, по крайней мере для этого примера. Необходимо только осуществить событие нажатия кнопки для кнопки отправки на сервер. В действительности это не совсем так. До сих пор мы не имеем никакой проверки для элемента управления календарем. Тем не менее, это просто, так как невозможно очистить выбор в этом элементе управления, и все, что мы должны сделать, это задать начальное значение. Это возможно в обработчике событий Page_Load()для создаваемой страницы: private void Page_Load(object sender, System.EventArgs e) { if (!this.IsPostBack) { calendar.SelectedDate = System.DateTime.Now; } } В данном случае мы просто выбираем сегодняшнюю дату в качестве начальной точки. Отметим, что мы сначала проверяем, что Page_Load()вызывается в результате операции обратной отправки, проверяя свойство страницы IsPostBack. Если выполняется обратная отправка, то это свойство будет true, и мы оставляем только выбранную дату (в конце концов, мы не хотим потерять выбор пользователя) Чтобы добавить обработчик нажатия кнопки, надо дважды щелкнуть мышью на кнопке и добавить следующий код: protected void submitButton_Click(object sender, System.EventArgs e) { if (this.IsValid) { resultLabel.Text = roomList.SelectedItem.Text + "has been booked on " + calendar.SelectedDate.ToLongDateString() + " by " + nameBox.Text + " for " + eventBox.Text + " event. "; foreach (ListItem attendee in attendeeList.Items) { if (attendee.Selected) { resultLabel.Text += attendee.Text + " , "; } } resultLabel.Text += " and " + nameBox.Text + "will be attending."; } } Здесь мы задаем для свойстваText элемента управления resultLabelрезультирующую строку, которая появится под основной таблицей. В IE результат после отправки может выглядеть следующим образом: Если встретятся ошибки, то вместо этого будет активизировано ValidationSummary: Включение обратной отправки без проверки Если поэкспериментировать с этим примером какое-то время, то можно заметить, что итоговая проверка появляется в том случае, если изменить дату до ввода каких-либо других данных. Это пример некоторого свойства, которое скорее всего будет раздражать пользователя, который сразу подумает: "Я ввел бы эти данные, если бы мне дали возможность это сделать!". Чтобы обойти этот момент, можно отключить итоговую проверку (используя свойство Enabled), если не нажата кнопка отправки. Однако это ведет к другой проблеме. Элемента управления проверкой способны предотвратить обратную отправку, например, когда нажата кнопка отправки, и динамически заполнить итоговую проверку на клиенте без обращения к серверу. Если отключить итоговую проверку в Page_Load()и включить ее в обработчике события нажатия кнопки, то итоговая проверка никогда не будет выводиться в браузерах, которые поддерживают клиентскую проверку (таких, как IE), так как только включение итоговой проверки будет перехватывать запросы обратной отсылки. Тогда мы должны отключить все элементы управления проверкой по умолчанию, затем включить заново эти элементы управления в обработчике нажатия кнопки и заставить их проверять, прежде чем мы проверим свойство формы IsValid. Для выполнения всего этого требуется следующее изменение кода. В Page_Load()мы отключаем все элементы управления проверкой. Мы можем использовать для этого коллекцию формы Validators, она содержит все элемента проверки на странице, поэтому мы можем их все перебрать. private void Page_Load(object sender, System.EventArgs e) validationSummary.Enabled = false; foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) { validator.Enabled = false; } … } В методе submitButton_Click()мы сразу включаем все элементы управления проверкой, за исключением итогового, что заставляет их проверять свои соответствующие элементы управления, вызывая метод формы isValid(), и затем проверять, как и раньше, свойство IsValid. Мы добавляем также в эту проверку предложение else, которое снова включает итоговую проверку, если оказывается, что IsValidзадан как false. Это предоставляет пользователю обратную связь тогда и только тогда, когда элементы управления имеют недопустимые данные и нажата кнопка отправки. protected void submitButton_Click(object sender, System.EventArgs e) { foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) { validator.Enabled = true; } this.Validated); if (this.IsValid) { … } else { validationSummary.Enabled = true; } } В результате этого эффективно отключается проверка на клиентской стороне, что стоит затраченных усилий, так как предоставляет дополнительное удобство использования. ADO.NET и связывание данныхСозданное в предыдущем разделе приложение формы Web является полностью функциональным, но содержит только статические данные. Кроме того, событие процесса заказа не содержит устойчивых данных события. Чтобы решить обе эти проблемы, можно воспользоваться ADO.NET и получить доступ к данным, хранящимся в базе данных, так чтобы можно было сохранять и извлекать данные события вместе со списками помещений и служителей. Соединение данных делает процесс извлечения данных еще легче. Элементы управления, такие как поля списков (и некоторые из более специальных элементов управления) готовы к использованию этой техники. Они могут быть связаны с любым объектом, который предоставляет интерфейс IEnumerable, ICollectionили IListSource, что включает объекты DataTable. В этом разделе мы начнем с модернизации нашего приложения события заказа помещения, а затем пойдем дальше и рассмотрим некоторые другие вещи, которые можно делать со связыванием данных, используя другие элементы управления Web. Модернизация приложения заказа помещенияСоздадим новое приложение Web с именем PCSWebApp3и скопируем код из созданного ранее приложения PCSWebApp2. Прежде чем начать новый код, давайте рассмотрим базу данных, к которой мы будем обращаться. База данныхДля этого примера используем базу данных Microsoft Access с именем PCSWebApp3.mdb, которую можно найти вместе с загружаемым кодом для этой книги. Для учета масштаба предприятия имеет смысл использовать базу данных SQL Server, но хотя используемая техника практически одинакова, но Access все же облегчает процесс тестирования. В ходе изложения будут показаны необходимые различия в коде, когда они возникнут. Представленная база данных содержит три таблицы: □ Attendeesсодержит список возможных почетных гостей событий. □ Roomsсодержит список возможных помещений для событий. □ Eventsсодержит список заказанных событий. Attendees Таблица Attendeesсодержит следующие столбцы:
База данных позволяет хранить сведения о 20 почетных гостях, каждый из которых может иметь адрес e-mail. Другое приложение может автоматически посылать письмо почетным гостям после выполнения заказа. Читателям предлагается реализовать такое приложение в качестве упражнения. RoomsТаблица Roomsсодержит следующие столбцы:
Таблица Eventsсодержит следующие столбцы:
Несколько событий представлены в загружаемой базе данных. Соединение с базой данныхДва элемента управления, которые мы хотели бы связать с данными, — attendeeListи roomList. Чтобы сделать это, мы должны задать свойства DataSourceэтих элементов управления как таблицы, содержащие данные. Код должен загрузить данные в эти таблицы и выполнить соединение. Оба эти элемента управления имеют также свойства DataTextFieldи DataValueField, которые определяют, какие столбцы использовать для вывода элементов списка и задания свойств value, соответственно. В обоих случаях можно задать эти свойства во время проектирования как Nameи ID, что будет использоваться, как только задается свойство DataSourceдля заполнения элементами списка элемента управления. Теперь мы можем сделать это в построителе форм Web. Удалите существующие записи из кода ASP.NET для этих элементов управления. Теперь что объявления будут выглядеть следующим образом: ... <asp:dropdownlist id="roomList" runat="server" width="160px" datatextfield="Room" datavaluefield="ID" / > ... <asp:listbox id="attendeeList" runat="server" width="160px" selectionmode="Multiple" rows="6" datatextfield="Name" datavaluefield=" " > Следующая задача состоит в создании соединения с базой данных. Существует несколько способов это сделать. Как мы видели в главе ADO.NET ранее, обычно для создания нового соединения используется окно Server Explorer. Так как мы работаем с Access, то тип провайдера для этого соединения будет Microsoft Jet 4.0 OLE DB Provider. Когда это будет задано в окне сервера, мы сможем перетащить соединение на форму Web, что добавит объект Data.OleDb.OleDbConnectionк форме с именем oleDbConnection1: public class WebForm1: System.Web.UI.Page { ... protected System.Data.OleDb.OleDbConnection oleDbConnection1; Для соединения SQL Server будет добавлен объект В метод InitializeComponent()также добавится код для задания свойства ConnectionStringформы oleDbConnection1, таким образом все будет готово для использования в коде. Мы хотим выполнить соединение данных в обработчике событий Page_Load(), так что элементы управления будут полностью заполнены, когда мы захотим использовать их в других частях кода. Приступаем к считыванию данных из базы данных, независимо от того, выполняется ли в данный момент операция обратной отправки (даже если элементы управления списком будут сохранять свое содержимое с помощью viewstate), чтобы гарантировать, что мы имеем доступ ко всем данным, которые могут понадобиться, хотя нам и не нужно выполнять само соединение данных при обратной отправке. Это может показаться слегка расточительным, но читатель при желании может в качестве упражнения добавить дополнительную логику к коду для оптимизации такого поведения. Здесь мы сосредоточимся на том, как заставить все работать, не входя в практические детали. Весь наш код будет помещен между вызовами методов Open()и Close()нашего объекта соединения: private void Page_Load(object sender, System.EventArgs e) { validationSummary.Enabled = false; foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) { validator.Enabled = false; } oleDbConnection1.Open(); if (!this.IsPostBack) { calendar.SelectedDate = System.DateTime.Now; } OleDbConnection1.Close(); } Мы вскоре увидим, почему задание данных календаря оставлено внутри этого кода проверки обратной отправки. Для обмена данными нам необходимо использовать несколько объектов хранения данных. Мы можем объявить их на уровне класса, чтобы мы имели к ним доступ из других функций. Нам понадобится объект DataSetдля хранения информации базы данных, три объекта OleDb.OleDbDataAdapterдля выполнения запросов на множестве данных и объект DataTableхранения событий для последующего доступа. Они объявляются следующим образом: public class WebForm1 : System.Web.UI.Page { ... protected System.Data.DataSet ds; protected System.Data.DataTable eventTable; protected System.DataOleDb.OleDbDataAdapter daAttendees; protected System.DataOleDb.OleDbDataAdapter daRooms; protected System.Data.OleDb.OleDbDataAdapter daEvents; Существуют версии SQL Server всех объектов OLE DB, и их использование идентично. Теперь для Page_Load()надо создать объект DataSet: private void Page_Load(object sender, System.EventArgs e) { ... oleDbConnection1.Open(); ds = new DataSet(); Затем мы должны присвоить объектам OleDbDataAdapterзапросы и соединить их с объектом соединения: ds = new DataSet(); daAttendees = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Attendees", oleDbConnection1); daRooms = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Rooms", oleDbConnection1); daEvents = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1); Затем мы выполняем запросы с помощью вызовов Fill(): daEvents = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1); daAttendees.Fill(ds, "Attendees"); daRooms.Fill(ds, "Rooms"); daEvents.Fill(ds, "Events"); Теперь переходим непосредственно к самому соединению данных. Как упоминалось ранее, это несложно и включает задание для свойства DataSourceэлементов управления соединением таблиц, с которыми мы хотим соединиться: daEvent s.Fill(ds, "Events"); attendeeList.DataSource = ds.Tables["Attendees"]; roomList.DataSource = ds.Tables["Rooms"]; Этот код задает свойства, но само соединение данных не произойдет, пока не будет вызван метод формы DataBind(), что мы сейчас и сделаем. Но прежде чем это сделать, заполним объект DataTableданными таблицы событий: roomList.DataSource = ds.Tables["Rooms"]; eventTable = ds.Tables["Events"]; Будем соединять данные только в том случае, если нет обратной отправки, иначе происходит просто обновление данных (которые, по предположению, являются статическими в базе данных в течение выполнения запроса заказа события). Соединение данных при обратной отправке будет также стирать выбранные значения в элементах управления roomListи attendeeList. Мы могли бы сделать об этом замечание перед соединением, а затем обновить их, но проще вызвать DataBind()в существующем операторе if(причина, почему этот оператор содержался в области кода, где открыто соединение с данными): eventTable = ds.Tables["Events"]; if (!this.IsPostBack) { calendar.SelectedDate = System.DateTime.Now; this.DataBind(); } oleDbConnection1.Close(); } Теперь выполнение приложения приведет к доступности всех данных о помещениях и приглашенных гостях из элементов управления соединением данных. Модификация элемента управления календаремПрежде чем обсуждать добавление событий к базе данных, давайте сделаем модификацию в выводе изображения календаря. Было бы неплохо выводить любой день, где была сделана предварительная заявка, другим цветом, и сделать такие дни недоступными для выбора. Это потребует модификации способа, которым даты задаются в календаре, и способа, которым выводятся ячейки дней. Начнем с выбора даты. Существует три момента, когда нам необходимо проверять даты заявок на мероприятия и изменять соответственно сделанный выбор: когда мы задаем начальную дату в Page_Load(), когда пользователь пытается выбрать дату из календаря, и когда сделана заявка на мероприятие и мы хотим задать новую дату, чтобы помешать пользователю заказать подряд два мероприятия в один день до выбора новой даты. Так как это должно быть общим свойством, можно создать также скрытый метод для выполнения этого вычисления. Этот метод должен принимать пробную дату в качестве параметра и возвращать дату для использования, которая будет либо той же датой, что и пробная, или следующим доступным днем после пробной даты. Код, который это делает, getFreeDate(), показан ниже: private System.DateTime getFreeDate(System.DateTime trialDate) { if (eventTable.Rows.Count > 0) { System.DateTime=testDate; bool trialDateOK = false; while (!trialDateOK) { trialDateOK = true; foreach (System.Data.DataRow testRow in eventTable.Rows) { testDate = (System.DateTime)TestRow["EventDate"]; if (testDate.Date == trialDate.Date) { trialDateOK = false; trialDate = trialDate.AddDays(1); } } } } return trialDate; } Этот простой код использует объект eventTable, который заполняется в Page_Load(), для извлечения данных о мероприятии. Сначала мы проверяем тривиальный случай, где не заказано никаких мероприятий, в этом случае мы можем просто подтвердить пробную дату, возвращая ее. Затем мы просматриваем даты в таблице Event, сравнивая их с пробной датой. Если мы находим совпадение, то добавляем один день к пробной дате и снова выполняем поиск. Извлечение даты из DataTableудивительно просто: testDate = (System.DateTime)testRow["EventDate"]; Преобразование данных столбца в System.DateTimeработает прекрасно. Итак, прежде всего используем getFreeDate()в Page_Load(). Это просто означает внесение небольшого изменения в код, который задает свойство календаря SelectedDate: if (!this.IsPostBack) { System.DateTime trialDate = System.DateTime.Now; calendar.SelectedDate = getFreeDate(trialDate); this.DataBind(); } Затем нам нужно ответить на выбор даты в календаре. Чтобы сделать это, просто добавим обработчик событий для события календаря SelectionChangedи выполним сравнение даты с датами существующих мероприятий: protected void calendar_SelectionChanged(object sender, System.EventArgs e) { System.DateTime trialDate = calendar.SelectedDate; calendar.SelectedDate = getFreeDate(trialDate); } Этот код идентичен коду в Page_Load(). Третий момент, когда необходимо выполнить эту проверку, возникает при нажатии кнопки заказа. Мы вернемся к этому немного позже, так как мы должны внести несколько изменений. Затем мы хотим изменить цвет ячейки дня календаря, чтобы обозначить существующие мероприятия. Чтобы сделать это, необходимо добавить обработчик событий для события DayRenderобъекта календаря. Это событие происходит всякий раз при изображении отдельного дня и предоставляет нам доступ к объекту выводимой ячейки и дате этой ячейки с помощью свойств Cellи Date параметра DayRenderEventArgsфункции обработчика. Нам нужно просто сравнить дату изображаемой ячейки с датами в объекте eventTableи покрасить ячейку с помощью свойства Cell.BackColor, если существует совпадение: protected void calendar_DayRender(object sender, System.Web.UI.WebControls.DayRenderEventArgs e) { if (eventTable.Rows.Count > 0) { System.DateTime testDate; fоreach (System.Data.DataRow testRow in eventTable.Rows) { testDate = (System.DateTime)testRow["EventDate"]; if (testDate.Date == e.Day.Date) { e.Cell.BackColor = Color.Red; } } } } В данном случае используется красный цвет, который дает нам следующее изображение: Здесь 15, 27, 28, 29 и 30 марта содержат мероприятия, а пользователь выбирает 17 марта. Теперь, после добавления логики выбора даты, невозможно выбрать день, который показан красным цветом, и если делается такая попытка, то вместо этого будет выбрана следующая дата. Например, щелчок на 28 марта в показанном выше календаре приведет к выбору 31 марта. Запись мероприятий в базу данныхОбработчик событий submitButton_Click()в настоящее время собирает строку из характеристик мероприятия и выводит ее в элементе управления resultLabel. Чтобы добавить мероприятие в базу данных, нужно реформатировать созданную строку в запрос SQL INSERTи выполнить его. Большая часть следующего кода выглядит знакомо: protected void submitButton_Click(object sender, System.EventArgs e) { foreach (System.Web.UI.WebControls.WebControl validator in this.Validators) { validator.Enabled = true; } this.Validate(); if (this.IsValid) { String attendees = ""; foreach (ListItem attendee in attendeeList.Items) { if (attendee.Selected) { attendees += attendee.Text + " (" + attendee.Value + "), "; } } attendees += " and " + nameBox.Text; String dateString = calender.SelectedDate.Date.Date.ToShortDateString(); String oleDbCommand = "INSERT INTO Events (Name, Room, " + "AttendeeList, EventDate) VALUES ('" + eventBox.Text + "', '" + roomList.SelectedItem.Value + "', '" + attendees + "', '" + dateString + "')"; После создания строки запроса SQL можно использовать ее для построения объекта OleDb.OleDbCommand: System.Data.OleDb.OleDbCommand insertCommand = new System.Data.OleDb.OleDbCommand(oleDbCommand, oleDbConnection1); После этого снова открываем соединение, которое было закрыто в Page_Load()(это снова, возможно, не самый эффективный способ реализации, но он прекрасно подходит для целей демонстрации) и выполняем запрос: oleDbConnection1.Open(); int queryResult = insertCommand.ExecuteNonQuery(); Метод ExecuteNonQuery()возвращает целое число, определяющее, сколько строк таблицы были изменены запросом. Если оно равно 1, то это означает, что вставка была успешной. Если это так, то мы помещаем сообщение об успешном выполнении в resultLabel, выполняем новый запрос для повторного заполнения eventTableи множества данных новым списком мероприятий (мы очищаем сначала множество данных, иначе события будут дублироваться) и изменяем выбор в календаре на новую, свободную, дату: if (queryResult == 1) { resultLabel.Text = "Event Added."; daEvents = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events" oleDbConnection1); dsClear(); daEvents.Fill(ds, "Events"); eventTable = ds.Tables["Events"]; calendar.SelectedDate = getFreeDate(calendar.SelectedDate.AddDays(1)); } Если ExecuteNonQuery()возвращает число, отличное от единицы, то это говорит о том, что возникла проблема. В данном примере мы не будем это затрагивать, а просто выведем уведомление об отказе в resultLabel: else { resultLabel.Text = "Event not added due to DB access " + "problem."; } И в конце мы снова закрываем соединение: oleDbConnection1.Close(); } else { validationSummary.Enabled = true; } } Таким образом, версия с доступом к базе данных приложения для предварительной заявки мероприятий завершена.
Еще о связывании данныхКогда мы рассматривали ранее в этой главе доступные серверные элементы управления, мы видели три из них, которые имеют дело с выводом данных: DataGrid, Repeaterи DataList. Все они будут очень полезны при выводе данных на страницу Web, так как они автоматически выполняют многие задачи, которые иначе потребовали бы большого объема кодирования. Прежде всего рассмотрим простейший элемент DataGrid. В качестве примера этого элемента управления давайте добавим вывод данных мероприятия в нижней части окна приложения PCSWebApp3. Это позволяет проигнорировать соединения с базой данных, так как они уже настроены для этого приложения: Добавим следующие строки в нижней части PCSWebApp3.aspx: <br>Results: <asp:label id=resultLabel Runat="server" Text="None.">None.</asp:label> <br> <br> <asp:DataGrid Runat="server" ID="eventDetails1" /> </form> </body> </HTML> А следующий код добавим в метод Page_Load()в PCSWebApp3.aspx.cs: attendeeList.DataSource = ds.Tables["Attendees"]; roomList.DataSource = ds.Tables["Rooms"]; eventTable = ds.Tables["Events"]; eventDetails1.DataSource = eventTable; if (!this.IsPostBack) { calendar.SelectedDate = System.DateTime.Now; this.DataBind(); } else { eventDetails1.DataBind(); } oleDbConnection1.Close(); } Отметим, что список мероприятий может изменяться между запросами в случае, если другой пользователь добавляет мероприятие, поэтому нам необходимо вызывать DataBind()для DataGrid, чтобы отразить эти изменения. Помните, что вызов DataBind()для всей форме приведет к потере выбранного помещения и почетного гостя, это честный компромисс. Если снова загрузить приложение в браузер Web, то можно увидеть ниже раздела данных заявки полный список мероприятий:
Мы можем сделать также еще одну модификацию в submitButton_Click(), чтобы гарантировать, что эти данные обновляются, когда добавляются новые записи: if (queryResult == 1) { resultLabel.Text = "Event Added."; daEvents = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM Events", oleDbConnection1); ds.Clear(); daEvents.Fill(ds, "Events"); eventTable = ds.Tables["Events"]; calendar.SelectedDate = getFreeDate(calendar.SelectedDate.AddDaysd)); eventDetails1.DataBind(); } Отметим, что мы вызываем DataBind()на DataGrid, а не на this. Это препятствует обновлению любых данных элементов управления соединения, что было бы не нужно. Все элементы управления соединением данных поддерживают этот метод, который обычно вызывается формой, если вызывается метод верхнего уровня (this)dataBind(). Элемент управления DataGridсодержит немало свойств, которые можно использовать для форматирования выводимых данных в более удобном для пользователя виде, но это оставляется читателю для самостоятельного рассмотрения. Вывод данных с помощью шаблоновДва других элемента управления выводом данных — Repeaterи DataList, требуют использования шаблонов для форматирования данных для вывода. Шаблонами в смысле ASP.NET являются параметризованные разделы кода HTML, которые используются как элементы вывода в некоторых элементах управления. Они позволяют точно определить, как данные выводятся в браузере, и могут создать без существенных усилий профессионально сделанное представление. Существует несколько шаблонов для настройки различных аспектов поведения списка, но шаблоном, который является важным для Repeaterи DataList, является шаблон <ItemTemplate>, используемый при выводе каждого элемента данных. Мы объявляем этот шаблон (и все остальные) внутри объявления элемента управления, например: <asp:DataList Runat="server" ... > <ItemTemplate> ... </ItemTemplate> </asp:DataList> Внутри объявлений шаблонов нам нужно будет выводить разделы HTML, включая параметры с данными, связанными с элементом управления. Существует специальный синтаксис, который можно использовать для вывода таких параметров:
DataBinder.Eval(). Эта полезная функция может использоваться для вывода данных из таблицы, связанной с элементом управления, определяя столбец с помощью следующего синтаксиса: <%# DataBinder.Eval(Container.DataItem, "ColumnName") %> Существует также необязательный третий параметр, позволяющий сформатировать возвращаемые данные, который имеет синтаксис, идентичный выражениям форматирования строк, используемым в других местах. Полное описание доступных шаблонов дано в следующей таблице:
Рассмотрим это на примере. Используем для него запрос существующих данных в PCSWebApp3. Пример Расширим таблицу вверху страницы, чтобы она содержала DataList, выводящий каждое из мероприятий, хранящихся в базе данных. Мы сделаем эти мероприятия выбираемыми, чтобы данные любого мероприятия можно было вывести, щелкая на его имени. Изменения в коде в PCSWebApp3показаны ниже: <tr> <td align=middle colSpan=3> <asp:validationsummary id=validationSummary Runat="server" headertext="Before submitting your request:" /> </td> </tr> <tr> <td align-left colSpan=3 width="100%"> <table cellspacing=4> <tr> <td width="40%" bgcolor="#ccffcc" > <asp:DataList Runat="server" ID="eventDetails2" OnSelectedIndexChanged="eventDetails2_SelectedIndexChanged" > <ItemTemplate> <asp:LinkButton Runat="server" CommandName="Select" forecolor="#0000ff" ID="Linkbutton1"> <%# DataBinder.Eval(Container.DataItem, "Name") %> </asp:LinkButton> <br> </ItemTemplate> <SelectedItemTemplate> <b><%# DataBinder.Eval(Container.DataItem, "Name") %></b> <br> </SelectedItemTemplate> </asp:DataList> </td> <td valign="top"> <asp:Label Runat="server" ID="edName" Font-Name="Arial" Font-Bold="True" Font-Italic="True" Font-Size="14"> Select an event to view details. </asp:Label> <br> <asp:Label Runat="server" ID="edDate" /> <br> <asp:Label Runat="server" ID="edRoom" /> <br> <asp:Label Runat="server" ID="edAttendees" /> </td> </tr> </table> </td> </tr> </table> Здесь мы добавили новую строку таблицы, содержащую сведения с DataListв одном столбце и представленные данные в другом. Представление данных является просто четырьмя метками для свойств мероприятия, одна из которых содержит текст "Select an event to view details", когда не выбрано никакого мероприятия (ситуация при первой загрузке формы). DataListиспользует <ItemTemplate>и <SelectedItemTemplate>для вывода данных мероприятия. Чтобы облегчить выбор, мы инициируем команду Selectвнутри <ItemTemplate>, что автоматически изменяет выбор. Мы используем также для заполнения данными мероприятия событие OnSelectedIndexChanged, которое включается, когда команда Selectизменяет выбор. Обработчик событий для этого показан ниже (Отметим, что нам нужно сначала выполнить метод DataBind()для обновления выбора): protected void eventDetails2_SelectedIndexChanged(object sender, System.EventArgs e) { eventDetails2.DataBind(); DataRow selectedEventRow = eventTable.Rows[eventDetails2.SelectedIndex]; edName.Text = (string)selectedEventRow["Name"]; edDate.Text = "<b>Date:</b> " + ((DateTime)selectedEventRow["Event Date"]).ToLongDateString(); edAttendees.Text = "<b>Attendees:</b> " + (string)selectedEventRow["AttendeeList"]; DataRow selectedEventRoomRow = ds.Tables["Rooms"].Rows[(int)selectedEventRow["Room"] -1]; edRoom.Text = "<b>Room:</b> " + selectedEventRoomRow["Room"]; } Здесь данные в dsи eventTableиспользуются для заполнения деталями мероприятия. Как и в случае DataGridранее, необходимо в Page_Load()задать и связать данные для eventDetails2: eventDetails1.DataSource = eventTable; eventDetails2.DataSource = eventTable; ... eventDetails1.DataBind(); eventDetails2.DataBind(); И заново связать в submitButton_Click(): eventDetails1.DataBind(); eventDetails2.DataBind(); Детали мероприятия представлены в таблице: О шаблонах и элементах управления связыванием данных можно было бы написать целую книгу. Однако изложенного материала в данном разделе вполне достаточно для того, чтобы начать работать. Конфигурация приложенияВ этой главе без каких-либо подробностей упоминалось о существовании концептуального приложения, содержащего страницы и конфигурационные настройки. Это важная концепция для восприятия, особенно при конфигурировании web-сайтов множеством одновременных пользователей. Сразу сделаем несколько замечаний о терминологии и времени жизни приложения. Приложение определяется как все файлы проекта, оно сконфигурировано с помощью файлов web.config. Объект Applicationсоздается, когда приложение запускается в первый раз, что происходит, когда поступает первый запрос HTTP. В это время также срабатывает событие Application_Start, обработчик событий для которого детально описывается в global.asax(вместе со всеми другими событиями, обсуждаемыми здесь), и создается пул экземпляров HttpApplication. Каждый входящий запрос получает один из этих экземпляров, который выполняет обработку запроса (это означает, что объекты HttpApplicationне нуждаются в копировании при одновременном доступе, в отличие от глобального объекта Application). Когда все экземпляры HttpApplicationзаканчивают свою работу срабатывает событие Application_End, и приложение прекращается, разрушая объект Application. Когда отдельный пользователь использует приложение Web, запускается сеанс. Как и в случае приложения, это включает создание специфического для пользователя объекта Sessionвместе с включением события Session_Start. В течение сеанса в отдельные запросы могут входить события Application_BeginRequestи Application_EndRequest. Это повторяется несколько раз за сеанс, когда в приложении происходит доступ к различным ресурсам. Отдельные сеансы могут прекращаться вручную или будут прерываться, если не получают больше никаких запросов. Прекращение сеанса включает событие Session_Endи разрушение объекта Session. Как этот процесс может нам помочь? Существует несколько вещей, которые можно сделать, чтобы рационализировать приложение. Вернемся к приложению, которое разрабатывалось в этой главе. Каждый раз при доступе к странице .aspx множество записей заполняется содержимым PCSWebApp3.mdb. Это множество записей всегда используется только для считывания данных, так как для добавления мероприятий в базу данных используется другой метод. В таких случаях можно заполнить множество записей в обработчике событий Application_Startи сделать его доступным для всех пользователей. Единственный раз, когда понадобиться обновить множество записей, возникнет, если будет добавлено событие. Это существенно повышает производительность, так как в большинстве запросов не будет требоваться доступ к базе данных. Другая техника, которую можно использовать, состоит в хранении информации уровня сеанса для использования отдельными пользователями между запросами. Это включает извлечение специфической для пользователя информации из хранилища данных при первом соединении пользователя, которая будет доступна, пока пользователь не прекратит посылать запросы или явно выйдет из системы. Такие технические приемы не будут здесь рассматриваться подробно, так как лучше использовать для этого специальные книги, посвященные ASP.NET, но это поможет, тем не менее, более широкому пониманию процессов. В следующей главе, имеющей дело со службами Web, мы увидим некоторые из этих технологий в действии. ЗаключениеВ этой главе был представлен обзор создания приложения Web с помощью ASP.NET. Мы видели, как можно использовать информацию о C#, которая была дана в этой книге совместно с элементами управления сервера Web для получения насыщенной среды разработки. Мы разработали приложение для заказа помещения для проведения мероприятий, чтобы проиллюстрировать многие из доступных технологий, таких как существующие различные серверные элементы управления и соединение данных с ADO.NET. В следующих двух главах мы рассмотрим две более важные темы Web: службы Web и специальные элементы управления. Мы продолжим разработку примера из этой главы, развивая его в различных направлениях, чтобы проиллюстрировать инструменты, имеющиеся в нашем распоряжении. В заключение можно сказать, что ASP.NET является новым оружием в арсенале разработчика Web: серверная обработка на данный момент является непревзойденной, а мощнейшие возможности C# и платформы .NET очень привлекательны для пользователей. |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|