Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
46
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

5.9. Связывание данных

Под связыванием данных (data binding) будем понимать помещение данных из некоего источника в элемент управления на странице. При разработке страницы применение связывания подразумевает два этапа:

  • указание на странице или в свойстве элемента управления источника данных;

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

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

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

  • <%# имя-свойства %>

  • <%# вызов-метода(. . .) %>

  • <%# выражение %>

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

<%@ Page Language="C#" %>

<script runat="server">

void Page_Load(object sender, EventArgs e) {

DataBind();

}

</script>

<html>

<body>

<form id="Form1" runat="server">

<asp:TextBox ID="TB1" runat="server" />

<br>

<asp:Label ID="Lb1" runat="server">

<%# TB1.Text%>

</asp:Label>

<br>

<asp:Button ID="B1" runat="server" Text="Button" />

</form>

</body>

</html>

В данной странице при каждой загрузке выполняется метод DataBind(). Содержимое метки Lb1 задано через синтаксис одиночного связывания. Это означает, что при каждой загрузке страницы содержимое метки будет равно содержимому текстового поля TB1.

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

Рис. 21. Схема связывания данных

На рисунке 22 показана страница, в которой два списковых элемента заполнены данными на основе содержимого объекта ArrayList:

Рис. 22. Связывание с объектом ArrayList

Исходный код страницы приведен ниже:

<%@ Page Language="C#" %>

<script runat="server">

void Page_Load(object sender, EventArgs e) {

if (!Page.IsPostBack) {

ArrayList vals = new ArrayList();

vals.Add("Yes");

vals.Add("No");

vals.Add("I dont know");

CBList.DataSource = vals;

LB.DataSource = vals;

DataBind();

}

}

</script>

<html>

<body>

<form id="Form1" runat="server">

<asp:CheckBoxList ID="CBList" runat="server" />

<br>

<asp:ListBox ID="LB" runat="server" />

</form>

</body>

</html>

Обратите внимание на следующий момент, характерный для связывания данных в aspx-страницах. Любой элемент управления, допускающий связывание, имеет локальный кэш данных, в котором хранит копию связываемых данных. Именно поэтому в методе Page_Load() выполняется проверка свойства страницы IsPostBack. Нет необходимости создавать массив и выполнять связывание при каждой загрузке; достаточно сделать это один (первый) раз.

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

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="server">

void Page_Load(object sender, EventArgs e) {

if(!Page.IsPostBack) {

SqlConnection con = new SqlConnection();

con.ConnectionString = "Server=(local);" +

"Database=CD_Rent;Integrated Security=SSPI";

SqlCommand cmd = new SqlCommand(

"SELECT name FROM Artists", con);

// Открываем соединение и получаем ридер

con.Open();

SqlDataReader r = cmd.ExecuteReader();

CBList.DataSource = r;

CBList.DataTextField = "name";

DataBind();

con.Close();

}

}

</script>

<html>

<body>

<form id="Form1" runat="server">

<asp:CheckBoxList ID="CBList" runat="server" />

</form>

</body>

</html>

В состав компонентов ASP.NET входят три шаблонных элемента управления: Repeater, DataList и DataGrid. Шаблон (template) описывает отдельную строку спискового элемента, позволяя настраивать внешний вид этой строки. При выводе шаблон повторяется требуемое число раз, в зависимости от количества строк в источнике данных.

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

Таблица 48

Описание шаблонов

Шаблон

Описание

DataGrid

DataList

Repeater

ItemTemplate

Генерирует внешний вид элемента данных

Да

Да

Да

AlternatingItemTemplate

Если задан, то чередуется с ItemTemplate

Нет

Да

Да

HeaderTemplate

Генерирует вид заголовка столбца

Да

Да

Да

FooterTemplate

Генерирует внешний вид колонтитула столбца

Да

Да

Да

SeparatorTemplate

Генерирует вид разделителя элементов данных

Нет

Да

Да

EditItemTemplate

Генерирует вид редактируемого элемента данных

Да

Да

Нет

Чтобы понять принципы использования шаблонов рассмотрим следующий пример. Пусть при помощи элемента DataList планируется вывести на странице содержимое таблицы Disks из базы CD_Rent. При этом нужно вывести заголовок таблицы, разделить строки, а год выпуска альбома отобразить красным цветом. Вот код aspx-страницы, которая решает поставленную задачу:

<%@ Page Language="C#"%>

<%@ Import Namespace="System.Data" %>

<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="server">

void Page_Load(object sender, EventArgs e) {

if(!Page.IsPostBack) {

SqlConnection con = new SqlConnection();

con.ConnectionString = "Server=(local);" +

"Database=CD_Rent;Integrated Security=SSPI";

SqlCommand cmd = new SqlCommand(

"SELECT name FROM Artists", con);

con.Open();

SqlDataReader r = cmd.ExecuteReader();

DataList1.DataSource = r;

DataBind();

con.Close();

}

}

</script>

<html>

<body>

<form id="form1" runat="server">

<div>

<asp:DataList ID="DataList1" runat="server">

<HeaderTemplate>

Data from Disks

</HeaderTemplate>

<ItemTemplate>

<asp:TextBox id=_name runat="server"

Text = '<%#((IDataRecord)Container.DataItem)["title"]%>'/>

<asp:Label id=_year ForeColor="Red" runat="server"

Text='<%#((IDataRecord)Container.DataItem)["release_year"]%>'/>

</ItemTemplate>

<SeparatorTemplate><hr /></SeparatorTemplate>

</asp:DataList>

</div>

</form>

</body>

</html>

Рис. 23. Вывод данных при помощи шаблонов

Рассмотрим принципы, по которым происходит связывание данных в шаблонных элементах управления. Каждое выражение связывания данных, встретившееся в шаблоне, преобразуется в экземпляр специального класса DataBoundLiteralControl и добавляется как дочерний элемент управления в шаблонный элемент управления (в нашем примере – в элемент DataList). Кроме этого, синтаксический анализатор страницы генерирует обработчик, подключаемый к событию DataBinding класса DataBoundLiteralControl:

public void @__DataBinding__control4(

object sender, System.EventArgs e) {

TextBox dataBindingExpressionBuilderTarget;

DataListItem Container;

dataBindingExpressionBuilderTarget = (TextBox)sender;

Container =

(DataListItem)dataBindingExpressionBuilderTarget.BindingContainer;

dataBindingExpressionBuilderTarget.Text =

System.Convert.ToString(((IDataRecord)Container.DataItem)["title"],

System.Globalization.CultureInfo.CurrentCulture);

}

Событие DataBinding класса DataBoundLiteralControl генерируется один раз для каждой строки источника данных во время вызова метода DataBind(). Код, сгенерированный для обработчика события, подготавливает локальную переменную Container, присваивая ей значение свойства BindingContainer элемента управления, сгенерировавшего событие. Свойство BindingContainer возвращает ссылку на текущий шаблон ItemTemplate, который для класса DataListItem является экземпляром класса DataListItem. И, наконец, для доступа к текущей строке источника данных класс DataListItem предоставляет свойство DataItem. Поскольку элемент управления DataList связывается со считывателем SqlDataReader, источник данных предоставлет интерфейс IDataRecord, который можно использовать для доступа к текущему значению столбца title.

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

<ItemTemplate>

<asp:TextBox id=_name runat="server"

Text= '<%# DataBinder.Eval(Container.DataItem, "title")%>'/>

<asp:Label id=_year ForeColor="Red" runat="server"

Text = '<%# DataBinder.Eval(Container.DataItem, "release_year")%>' />

</ItemTemplate>

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

public void ExecuteSQLStatement(string strSQL) {

SqlConnection con = new SqlConnection(. . .);

SqlCommand cmd = new SqlCommand(strSQL, con);

// Выполняем команду

con.Open();

cmd.ExecuteNonQuery();

con.Close();

}

Следующий метод будет использоваться, чтобы получить данные из базы и связать их с компонентом DataList:

public void BindDataList() {

// Создаем соединение

SqlConnection con = new SqlConnection(. . .);

// Создаем команду для получения всех данных таблицы

SqlCommand cmd = new SqlCommand("SELECT * FROM Artists", con);

// Открываем соединение и получаем ридер, выполняя команду

con.Open();

SqlDataReader r = cmd.ExecuteReader();

// Выполняем связывание (DList – идентификатор DataList)

DList.DataSource = r;

DList.DataBind();

con.Close();

}

Четыре метода будут обработчиками событий соответствующих команд DataList:

public void DoItemEdit(object source,

DataListCommandEventArgs e) {

// Указываем строку, которую переводим в режим

// редактирования и "пересвязываем" данные

DList.EditItemIndex = e.Item.ItemIndex;

BindDataList();

}

void DoItemUpdate(object source, DataListCommandEventArgs e) {

// Ищем в строке элемент управления

TextBox tb = (TextBox)e.Item.FindControl("TB");

// Формируем SQL-команду

string strSQL = "UPDATE Artists SET name='" + tb.Text +

"' WHERE id='"+DList.DataKeys[e.Item.ItemIndex]+"'";

// Выполняем команду

ExecuteSQLStatement(strSQL);

// Показываем, что строка больше не редактируется

DList.EditItemIndex = -1;

// "Пересвязываем" данные

BindDataList();

}

public void DoItemCancel(object source,

DataListCommandEventArgs e) {

// Показываем, что строка больше не редактируется

DList.EditItemIndex = -1;

// "Пересвязываем" данные

BindDataList();

}

public void DoItemDelete(object source,

DataListCommandEventArgs e) {

//Формируем и выполняем SQL-команду

string strSQL = "DELETE FROM Artists WHERE id='" +

DList.DataKeys[e.Item.ItemIndex] + "'";

ExecuteSQLStatement(strSQL);

DList.EditItemIndex = -1;

BindDataList();

}

Отдельно представим шаблоны для заголовка DataList, элемента (с кнопкой Edit), элемента в режиме редактирования (поле ввода, кнопки Update, Delete, Cancel), разделителя:

<HeaderTemplate>

<b>Information about Artists</b>

</HeaderTemplate>

<ItemTemplate>

<asp:Button ID="B1" CommandName="Edit" runat="server" Text="Edit" />

ID: <%# DataBinder.Eval(Container.DataItem, "id") %>  

Name: <%# DataBinder.Eval(Container.DataItem, "name") %>

</ItemTemplate>

<EditItemTemplate>

ID: <%# DataBinder.Eval(Container.DataItem, "id") %>  

<asp:Button ID="B2" CommandName="Update" runat="server"

Text="Update" />

<asp:Button ID="B3" CommandName="Delete" runat="server"

Text="Delete" />

<asp:Button ID="B4" CommandName="Cancel" runat="server"

Text="Cancel" /> <br />

Name: <asp:TextBox ID="TB" runat="server"

Text='<%# DataBinder.Eval(Container.DataItem, "name") %>'/>

</EditItemTemplate>

<SeparatorTemplate> <hr /> </SeparatorTemplate>

В свойствах DataList настроим внешний вид, укажем ключевое поле, и укажем обработчики команд (связь между обработчиком и соответствующей кнопкой устанавливается автоматически, если у кнопки задано правильное свойство CommandName):

<asp:DataList ID="DList" runat="server" CellSpacing="2"

OnEditCommand="DoItemEdit"

OnUpdateCommand="DoItemUpdate"

OnDeleteCommand="DoItemDelete"

OnCancelCommand="DoItemCancel" DataKeyField="ID">

Рис. 24 демонстрирует страницу в режиме редактирования второй строки.

Рис. 24. Страница в режиме редактирования

5.10. Web-приложение. Файл global.asax

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

Каждое web-приложение содержит, по крайней мере, один aspx-файл. Кроме этого, в состав приложения могут входить следующие файлы:

  • Единственный файл global.asax, размещаемый в корневом каталоге приложения.

  • Один или несколько файлов конфигурации web.config. Если web-приложение содержит подкаталоги, то допускается не более одного файла web.config на подкаталог.

  • Один или несколько файлов, описывающих пользовательские элементы управления (расширение *.ascx).

  • Один или несколько файлов классов для поддержки технологии Code-Behind.

  • Поддиректорию /bin, содержащую сборки, автоматически подключаемые к приложению.

  • Файлы любых других типов (*.htm, *.asp, изображения и т.д.).

ASP.NET поддерживает глобальный файл для каждого web-приложения – файл global.asax. Этот файл играет роль пункта реализации глобальных событий, объектов и переменных. Общий формат файла global.asax следующий:

<%@ директива атрибут = значение %>

<script runat="server">

[обработчики событий приложения]

</script>

Файл global.asax поддерживает три директивы: @Application, @Import, @Assembly. Директива @Application позволяет определить базовый класс, на основе которого создается класс приложения (атрибут Inherits), указать язык программирования для серверного кодаобработчиков событий (атрибут Language), а также задать описание приложения (атрибут Description). Директива @Import позволяет импортировать пространства имен для использования в global.asax. Директива @Assembly используется для подключения сборок к приложению (сборки из поддиректории \bin подключаются автоматически).

Каждое web-приложение описывается объектом класса, производного от класса HttpApplication. Свойства данного класса описаны в таблице 49.

Таблица 49

Свойства класса HttpApplication

Имя свойства

Описание

Application

Объект класса HttpApplicationState, описывающий состояние web-приложения

Context

Объект класса HttpContex, описывающий контекст запроса

Modules

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

Request

Ссылка на объект HttpRequest, обеспечивающий доступ к информации о HTTP-запросе

Response

Ссылка на объект HttpResponse, обеспечивающий доступ к информации о HTTP-ответе

Server

Объект класса HttpServerUtility, описывающий параметры web-сервера

Session

Ссылка на объект класса HttpSessionState, хранящий данные текущей сессии пользователя в web-приложении

User

Ссылка на объект, реализующий интерфейс IPrincipal и описывающий пользователя. Свойство используется при проведении аутентификации

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

Таблица 50

События web-приложения

Событие

Причина срабатывания

Последова-

тельность

BeginRequest

Получение нового запроса

1

AuthenticateRequest

Завершение аутентификации пользователя

2

AuthorizeRequest

Завершение авторизации пользователя

3

ResolveRequestCache

Генерируется после авторизации, но перед запуском обработчика. Используется модулями кэширования для отмены выполнения обработчиков запроса, если в кэше есть нужная запись

4

AcquireRequestState

Загрузка состояния сеанса

5

PreRequestHandlerExecute

Перед передачей запроса обработчику

6

PostRequestHandlerExecute

Завершение обработчика запроса

7

ReleaseRequestState

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

8

UpdateRequestCache

После завершения обработчика. Используется модулями кэширования для сохранения ответа в кэше

9

EndRequest

После обработки запроса

10

Disposed

Перед закрытием приложения

Error

При наступлении необработанной исключительной ситуации

PreSendRequestContent

Перед передачей клиенту содержимого ответа

PreSendRequestHeaders

Перед передачей клиенту заголовков HTTP

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

Таблица 51

Особые события web-приложения

Событие

Причина срабатывания

Application_Start

Запуск приложения

Application_End

Завершение приложения

Session_Start

Начало сеанса пользователя

Session_End

Завершение сеанса пользователя

Приведем пример файла global.asax, содержащего обработчики событий BeginRequest и EndRequest:

<%@ Application Language="C#" %>

<script RunAt="server">

void Application_BeginRequest(object sender, EventArgs e) {

Response.Write("Request starts!" + "<br />");

}

void Application_EndRequest(object sender, EventArgs e) {

Response.Write("Request ends!" + "<br />");

}

</script>