Эндрю Троелсен - ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание
Изменение файла *.aspx
Обновите пользовательский интерфейс исходного файла *.aspx так, как показано на рис. 24.4.
В обработчике события Load страницы настройте GridView на отображение содержимого помещенного в кэш типа DataSet при первом обращении пользователя к странице.
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) {
carsGridView.DataSource = (DataSet)Cache["AppDataSet"];
carsGridView.DataBind();
}
}
Рис. 24.4. Графический интерфейс пользователя для приложения с кэшированием
В обработчике события Click кнопки Добавить эту машину вставьте новую запись в базу данных Cars, используя для этого объект ADO.NET SqlCommand. После добавления записи вызовите вспомогательную функцию RefreshGrid(), которая обновит интерфейс с помощью типа SqlDataReader (поэтому не забудьте указать using для пространства имен System.Data.SqlClient). Вот как должны выглядеть соответствующие методы,
protected void btnAddCar_Click(object sender, EventArgs e) {
// Обновление таблицы Inventory
// и вызов RefreshGrid().
SqlConnection cn = new SqlConnection();
cn.ConnectionString = "User ID=sa;Pwd=;Initial Catalog=Cars;Data Source=(local)";
cn.Open();
string sql;
SqlCommand cmd;
// Вставка нового Car.
sql = string.Format(
"INSERT INTO Inventory(CarID, Make, Color, PetName) VALUES" +
"('{0}', '{1}', '{2} ', '{3}')",
txtCarID.Text, txtCarMake.Text,
txtCarColor.Text, txtCarPetName.Text);
cmd = new SqlCommand(sql, cn);
cmd.ExecuteNonQuery();
cn.Close();
RefreshGrid();
}
private void RefreshGrid() {
// Заполнение таблицы.
SqlConnection cn = new SqlConnection();
cn.ConnectionString = "User ID=sa;Pwd=;Initial Catalog=Cars;Data Source=(local)";
cn.Open();
SqlCommand cmd = new SqlCommand("Select * from Inventory", cn);
carsGridView.DataSource = cmd.ExecuteReader();
carsGridView.DataBind();
cn.Close();
}
Теперь, чтобы проверить использование кэша, запустите два экземпляра вашего Web-браузера и перейдите к этой странице *.aspx. Вы должны увидеть, что оба типа DataGrid отображают одинаковую информацию. В окне одного из экземпляров браузера добавьте новую машину. Очевидно, что в результате вы увидите обновленные данные GridView в окне браузера, который инициировал обращение к источнику данных.
Во втором экземпляре браузера щелкните на кнопке Обновить. Вы не увидите новый элемент, поскольку обработчик события Page_Load читает данные непосредственно из кэша. (Если же вы увидели новые данные, это значит, что уже истекли 15 секунд. Либо печатайте быстрее, либо увеличьте время, в течение которого тип DataSet должен оставаться в кэше.) Подождите несколько секунд и снова щелкните на кнопке Обновить второго экземпляра браузера. Теперь вы должны увидеть новые данные, поскольку время пребывания DataSet в кэше истекло, и целевой метод делегата CacheItemRemovedCallback автоматически обновил тип DataSet, помещенный в кэш.
Как видите, главное преимущество типа Cache заключается в том, что вы получаете возможность ответить на удаление члена. В этом примере вы, конечно, можете избежать использования типа Cache путем чтения данных в обработчике Page_Load() непосредственно из базы данных Cars. Однако теперь вам должно быть ясно, что кэш позволяет автоматически обновлять данные с помощью делегатов .NET.
Замечание. В отличие от типа HttpApplicationState, класс Cache не поддерживает методы Lock() и Unlock(). Так что при необходимости обновить связанные элементы вам придется использовать типы из пространства имен System.Threading или ключевое слово lock C#.
Исходный код. Файлы примера CacheState размещены в подкаталоге, соответствующем главе 24.
Обработка сеансовых данных
Поговорив о данных уровня приложения, давайте перейдем к обсуждению данных, создаваемых на уровне пользователя. Как уже упоминалось, сеанс на самом деде представляет собой процесс взаимодействие пользователя с Web-приложением, представленный типом HttpSessionState. Для поддержки информации сеанса конкретного пользователя объект HttpApplication и любые другие типы System.Web.UI.Page могут использовать доступ к свойству Session. Классическим примером необходимости поддержки пользовательских данных является корзина покупателя: при подключении десятков посетителей к странице Интернет-магазина для каждого посетителя должен поддерживаться уникальный список товаров, которые этот посетитель собрался купить.
При регистрации нового пользователя в Web-приложении среда выполнения .NET автоматически назначит пользователю уникальный идентификатор сеанса, используемый для идентификации данного пользователя. С каждым идентификатором сеанса ассоциируется пользовательский экземпляр типа HttpSessionState, который будет содержать данные соответствующего пользователя. Технология добавления и чтения сеансовых данных синтаксически идентична работе c данными приложения, например:
// Добавление/чтение сеансовой переменной для данного пользователя.
Session["DesiredCarColor"] = "зеленый";
String color = (string)Session["DesiredCarColor"];
Производный от HttpApplication тип позволяет выполнить перехват событий начала и завершения сеанса с помощью обработчиков событий Session_Start() и Session_End(). В пределах Session_Start() вы можете создать любые элементы данных пользователя, а в Session_End() можно выполнить любые действия, необходимые при завершения сеанса пользователя.
‹%@ Application Language="C#" %›
‹script runat="server"›
…
void Session_Start(objecl sender, EventArgs e) {}
void Session_End(object sender, EventArgs e) {}
‹/script›
Подобно типу HttpApplicationState, тип HttpSessionState может содержать любой тип, производный от System.Object, включая пользовательские классы. Предположим, например, что у нас есть новое Web-приложение (SessionState), которое определяет вспомогательный класс с именем UserShoppingCart.
public class UserShoppingCart {
public string desiredCar;
public string desiredCarColor;
public float downPayment;
public bool isLessing;
public DateTime dateOfPickUp;
public override string ToString() {
return string.Format("Машина: {0}‹br›Цвет: {1}‹br›$ кредит: {2}‹br›" +
"Аренда: {3}‹br›Доставка: {4}",
desiredCar, desiredCarColor, downPayment, isLeasing,
dateOfPickUp.ToShortDateString());
}
}
В обработчике событий Session_Start() можно назначить каждому пользователю свой экземпляр класса UserShoppingCart.
void Session_Start(Object sender, EventArgs e) {
Session["UserShoppingCartInfo"] = new UserShoppingCart();
}
При просмотре ваших Web-страниц пользователем вы можете взять экземпляр UserShoppingCart и заполнить его поля данными соответствующего пользователя. Предположим, что у вас есть простая страница *.aspx с набором элементов ввода, соответствующих каждому полю типа UserShoppingCart, и кнопкой (Button), используемой для установки введенных значений (рис. 24.5).
Рис. 24.5. Графический интерфейс пользователя для приложения с сеансовыми данными
Серверный обработчик события Click действует весьма прямолинейно (считывает значения элементов TextBox и отображает поступающие значения в поле типа Label).
protected void btnSubmit_Click(object sender, EventArgs e) {
// Установка преференций текущего пользователя.
UserShoppingCart u = (UserShoppingCart)Session["UserShoppingCartInfo"];
u.DateOfPickUp = myCalendar.SelectedDate;
u.desiredCar = txtCarMake.Text;
u.desiredCarColor = txtCarColor.Text;
u.downPayment = float.Parse(txtDownPayment.Text);
u.isLeasing = chkisLeasing.Checked;
lblUserInfo.Text = u.ToString();
Session["UserShoppingCartInfo"] = u;
}
В Session_End() вы можете, например, сохранить значения полей UserShoppingCart в базе данных или выполнить какие-то иные действия. Так или иначе, если Вы запустите два или три экземпляра своего браузера, вы должны увидеть, что каждый пользователь может создать свою корзину покупателя, связанную с его уникальным экземпляром HttpSessionState.
Дополнительные члены HttpSessionState
Кроме индексатора типа, класс HttpSessionState определяет ряд других интересных членов. Во-первых, свойство SessionID возвращает уникальный идентификатор текущего пользователя.
lblUserID.Text = string.Format("Значение вашего ID: {0}", Session.SessionID);
Методы Remove() и RemoveAll() можно использовать для удаления элементов из экземпляра HttpSessionState пользователя.
Session.Remove["НекоторыеУжеНенужныеЭлементы"];
Тип HttpSessionState определяет также набор членов, управляющих значениями времени ожидания для текущего сеанса. Снова подчеркнем, что по умолчанию каждому пользователю позволяется 20 минут бездействия до того, как объект HttpSessinState будет уничтожен. Поэтому если пользователь войдет в ваше Web-приложение (и получит в результате этого свое уникальное значение идентификатора сеанса), но не будет обращаться к узлу в течение 20 минут, среда выполнения "решит", что пользователь больше не интересуется узлом и уничтожает все сеансовые данные этого пользователя. Вы имеете возможность изменить это принятое по умолчанию 20-минутное значение для каждого пользователя в отдельности, используя свойство Timeout. Чаще всего для такого изменения используется контекст метода Global.Session_Start().