ASP.NET 2.0. Обзор новых сервисов, элементов управления и средств

Исходные коды статьи (0,12 МБ), © Microsoft.com


Автор:
  • Джеф Просиз

Спустя четыре года после своего выхода ASP.NET стала «золотым стандартом» для Web-приложений, выполняемых на серверах под управлением Windows, а строка runat="server" прочно вошла в лексикон Web-разработчиков всего мира. Кроме того, ASP.NET позволила очертить контуры будущего Web-программирования — будущего, в котором важное место занимают серверные элементы управления, выполняющие рендеринг HTML-кода и сценариев и вызывающие события. В следующей версии Microsoft .NET Framework ASP.NET 2.0 выйдет из подросткового возраста и станет зрелой платформой. Ее разработчики рассчитывают сократить на 70% и более объем кода, необходимого для решения типичных задач Web-программирования. Добиться этого нелегко, но возможно, поскольку благодаря многочисленным новым сервисам, элементам управления и средствам у ASP.NET 2.0 есть все шансы стать столь же значительным усовершенствованием ASP.NET 1.x, каким ASP.NET 1.x была для ASP. Эта статья — развернутый обзор новшеств в ASP.NET 2.0. Чтобы лучше осветить ключевые средства, я рассмотрю некоторые области более подробно и приведу примеры программ. Все примеры кода скомпилированы и протестированы в версии ASP.NET 2.0, предшествовавшей бета-версии. Возможно, чтобы эти примеры работали в бета-версии, некоторые из них придется изменить.


 

Эта статья написана на основе предварительной версии ASP.NET 2.0 Community Technology Preview, вышедшей в марте 2004 г. Любая содержащаяся здесь информация может быть изменена.Community Technology Preview, вышедшей в марте 2004 г. Любая содержащаяся здесь информация может быть изменена.

В статье используются следующие технологии: ASP.NET 1.x и C#.

В статье рассматриваются:

  • новые элементы управления, работающие с данными;
  • администрирование и роли;
  • персонализация и темы;
  • написание кода и параметры компиляции.

Master Pages

Один из самых бросающихся в глаза недостатков ASP.NET 1.x — отсутствие поддержки шаблонов страниц. Нельзя определить «эталонную страницу» (master page), от которой наследуют другие страницы. Разработчики решают эту проблему, создавая страницы с помощью пользовательских элементов управления, которые легко воспроизводить на разных страницах. В ASP.NET 2.0 больше не нужно прибегать к таким приемам, поскольку появилось новое средство — Master Pages. Чтобы описать, что такое Master Pages, подойдет термин «визуальное наследование». Сначала вы определяете эталонную страницу, содержащую то, что должно присутствовать на других страницах, и с помощью элементов управления ContentPlaceHolder задаете места, в которые дочерние страницы (subpages) могут помещать свое содержимое. Затем разрабатываете дочерние страницы — ASPX-файлы, ссылающиеся на эталонную страницу через директивы вида:

<%@ Page MasterPageFile="~/Foo.master" %>

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

В приложении в листингах  и  эталонная страница используется, чтобы определить заголовок и колонтитул, показываемые на каждой странице. Дочерняя страница вставляет содержимое между заголовком и колонтитулом, добавляя элемент Content, ссылающийся на ContentPlaceHolder эталонной страницы. Обратите внимание на совпадение ID и ContentPlaceHolderID и на директиву @ Master в эталонной странице.

Листинг 1. Master Pages. Файл Master.master

<%@ Master %>

<html>
  <body leftmargin="0" topmargin="0" rightmargin="0"
    bottommargin="0" marginheight="0" marginwidth="0">
    <!-- Баннер -->
    <table cellspacing="0" cellpadding="0"
      style="background-image: url('/images/stripes.gif');
      background-repeat: repeat-x, repeat-y" width="100%">
      <tr><td align="center">
        <span style="font-family: verdana; font-size: 36pt;
          font-weight: bold; color: white">Master Pages</span><br>
        <span style="font-family: verdana; font-size: 10pt;
          font-weight: normal; color: white">Баннер определен
          в Master.master</span>
      </td></tr>
    </table>

    <!-- Поле подстановки для контента,
         помещаемого между баннером и колонтитулом -->
      <asp:ContentPlaceHolder ID="Main" RunAt="server" />
    </form>

    <!-- Колонтитул -->
    <table width="100%"><tr><td align="center">
      <span style="font-family: verdana; font-size: 8pt; color: red">
        Copyright (c) 2004 by Me Inc. Все права защищены<br>
        Колонтитул определен в Master.master
      </span>
    </td></tr></table>
  </body>
</html>

Листинг 2. Master Pages. Файл Subpage.aspx

<%@ Page MasterPageFile="~/Master.master" %>

<asp:Content ContentPlaceHolderID="Main" RunAt="server">
  <table width="100%" height="256px"><tr><td align="center">
    <h2>Содержимое определено в Subpage.aspx</h2>
  </td></tr></table>
</asp:Content>

Master Pages полностью поддерживается объектной моделью ASP.NET. В класс System.Web.UI.Page добавлено свойство Master, позволяющее дочерним страницам программно обращаться к своим эталонным страницам и определенным в них элементам управления. Допускается вложение эталонных страниц и включение в них контента по умолчанию, который могут переопределять дочерние страницы:

<asp:ContentPlaceHolder ID="Main" RunAt="server">
    Контент по умолчанию, показываемый в дочерних страницах,
    если он не переопределяется явным образом
</asp:ContentPlaceHolder>

Кроме того, в файле Web.config можно задать эталонную страницу, по умолчанию используемую приложением:

<configuration>
  <system.web>
    <pages masterPageFile="~/Foo.master" />
  </system.web>
</configuraion>

Разработчики дочерних страниц могут переопределять контент, используемый по умолчанию, и создавать собственные эталонные страницы. Изюминка Master Pages в том, что эта технология поддерживается Visual Studio 2005. При загрузке дочерней страницы IDE показывает содержимое, заданное в эталонной странице, блеклым цветом и не разрешает его изменять, а содержимое, заданное в дочерней странице, отображает обычными цветами и разрешает редактировать. Благодаря этому легко понять, где определено содержимое, и, если требуется отредактировать содержимое, относящееся к эталонной странице, вы просто открываете в IDE шаблон страницы.

Более полное описание Master Pages можно найти в статье Фрица Аньена (Fritz Onion), опубликованной в этом номере «MSDN Magazine».

Элементы управления — источники данных

Связывание с данными — замечательная возможность ASP.NET 1.x.  Несколько строк кода для связывания с данными, помещенные в нужное место, заменяют массу ASP-кода, выполняющего запрос к базе данных и многократно вызывающего метод Response.Write, чтобы представить результаты запроса в HTML-формате. В ASP.NET 2.0 связывание с данными станет еще проще. Во многих случаях писать код вообще не потребуется благодаря новым элементам управления — источникам данных (элементам источников данных) (data source controls), перечисленным во врезке «Новые элементы управления, планируемые в ASP.NET 2.0"».

Следующая страница DataSource1.aspx, используя связывание с данными в ASP.NET 2.0,  показывает выборку из базы данных Pubs (SQL Server):

<html>
  <body>
    <form runat="server">
      <asp:SqlDataSource ID="Titles" RunAt="server"
        ConnectionString="server=localhost;database=pubs;Integrated
                         Security=SSPI"
        SelectCommand="select title_id, title, price from titles" />
      <asp:DataGrid DataSourceID="Titles" RunAt="server" />
    </form>
  </body>
</html>

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

Конечно, на практике связывание с данными редко бывает настолько простым. Допустим, вам нужно кэшировать результаты запроса или выполнять запросы к базам данных, указывая параметры, выбираемые в других элементах управления. Страница в листинге 3 использует один SqlDataSource, чтобы заполнить раскрывающийся список названиями стран из таблицы Customers базы данных Northwind, и другой — чтобы заполнить DataGrid перечнем клиентов из страны, выбранной в списке. Заметьте: элемент <SelectParameters> указывает, что SqlDataSource элемента управления DataGrid берет значение @country из раскрывающегося списка. Также обратите внимание на атрибуты EnableCaching и CacheDuration элемента SqlDataSource, связанного с выпадающим списком. Эти атрибуты указывают, что результаты запроса SELECT DISTINCT кэшируются в течение 60 секунд.

Табл. 1. Новые элементы управления, планируемые в ASP.NET 2.0
Простые элементы
BulletedList
Показывает маркированный список элементов
FileUpload
Позволяет закачивать файлы через Web-страницы
HiddenField
Представляет скрытые поля (<input type="hidden">)
ImageMap
Представляет карты изображений (image maps), используемые в HTML
Элементы источников данных
AccessDataSource
Для связывания с данными баз Microsoft Access
ObjectDataSource
Для связывания с данными классов, реализованных в собственных уровнях доступа к данным
SiteMapDataSource
Для связывания с данными XML-карт сайта
SqlDataSource
Для связывания с данными баз SQL, использующих провайдеры ADO.NET
XmlDataSource
Для связывания с данными XML-документов
Элементы управления регистрацией
Login
Интерфейс для входа с указанием имени пользователя и пароля
LoginName
Отображает имена аутентифицированных пользователей
LoginStatus
Интерфейс для входа и выхода
LoginView
Показывает одно представление для пользователей, прошедших аутентификацию, и другое — для не прошедших
ChangePassword
Интерфейс для смены паролей
PasswordRecovery
Интерфейс для отправки забытых паролей по электронной почте
CreateUserWizard
Интерфейс для создания новых учетных записей
Элементы управления страницами
Content
Задает содержимое полей подстановки в страницах, наследуемых от Master Pages
DetailsView
Отображает в HTML-таблице данные отдельной записи
DynamicImage
Представляет динамически сгенерированные изображения
FormView
Элемент, связываемый с данными, который поддерживает шаблоны и предоставляет очень гибкий UI
GridView
«Супер-DataGrid»; отображает наборы записей в виде HTML-таблиц
Menu
Показывает выпадающие и восходящие (fly-out) меню
MultiView
Разбивает страницу на несколько логических представлений
SiteMapPath
Показывает пути к страницам
TreeView
Представляет иерархические данные как деревья, узлы которых можно свертывать и развертывать
View
Используется совместно с MultiView для определения индивидуальных представлений
Wizard
Помогает пользователю пошагово выполнять определенные процедуры
Мобильные элементы управления
Pager
Разбивает страницы на части для вывода на устройствах с небольшим экраном
PhoneLink
Набирает телефонные номера на устройствах, оснащенных телефоном

Листинг 3. DataSource2.aspx

<html>
  <body>
    <form runat="server">
      <asp:SqlDataSource ID="Countries" RunAt="server"
        ConnectionString="server=localhost;database=northwind;
          Integrated Security=SSPI"
        SelectCommand="select distinct country from customers order by
                      country"
        EnableCaching="true" CacheDuration="60" />
      <asp:SqlDataSource ID="Customers" RunAt="server"
          ConnectionString="server=localhost;database=northwind;
            Integrated Security=SSPI"
          SelectCommand="select * from customers
            where country=@country">
        <SelectParameters>
          <asp:ControlParameter Name="Country"
            ControlID="MyDropDownList"
            PropertyName="SelectedValue" />
        </SelectParameters>
      </asp:SqlDataSource>
      <asp:DropDownList ID="MyDropDownList" DataSourceID="Countries"
        DataTextField="country" AutoPostBack="true" RunAt="server" />
      <asp:DataGrid DataSourceID="Customers" RunAt="server" />
    </form>
  </body>
</html>

Эти примеры — лишь малая часть возможностей, предоставляемых элементами источников данных. Например, можно вызывать хранимые процедуры, указывать в качестве параметров запросов значения, получаемые из строк запросов, ввода пользователя, состояния сеанса или cookie, а также задавать, что должен использовать элемент — DataSet или DataReader. Поскольку элементы источников данных обладают функциональностью адаптеров данных, с их помощью можно даже обновлять базы данных. Думаю, к моменту финального выпуска ASP.NET 2.0 появится много материалов, посвященных элементам источников данных. Дино Эспозито (Dino Esposito) рассматривает их значительно детальнее, чем я; см. рубрику «На переднем крае» в этом номере «MSDN Magazine».

Прежде чем закончить с темой связывания с данными, замечу, что ASP.NET 2.0 поддерживает упрощенный синтаксис связывания с данными. Разработчикам для ASP.NET приходится писать громоздкие выражения вида:

<%# DataBinder.Eval (Container.DataItem, "title") %>
В ASP.NET 2.0 это выражение можно заменить на:
<%# Eval("title") %>

Помимо оператора Eval ASP.NET 2.0 поддерживает операторы XPath и XPathSelect, которые через XPath-выражения определяют местонахождение данных в XML-документах.

ASP.NET-страницы могут оказаться довольно скучными, если не использовать атрибуты, придающие броский внешний вид элементам управления. Но сейчас есть одна проблема: атрибуты применяются по одному за раз и нельзя создавать «темы» элементов управления, т. е. сразу задавать значения группы визуальных атрибутов. Позвольте вас познакомить с темами и скинами — новым средством ASP.NET 2.0, облегчающим создание красиво оформленных страниц.

Чтобы увидеть, как работают темы и скины, добавьте в код DataSource2.aspx, показанный в листинге 3, сстроку:

<%@ Page Theme="BasicBlue" %>

Затем обновите страницу. Вы получите результат, как на рис. 1.

Изображение GIF  Рис. 1. Применение тем и скинов

Теперь придадим странице совершенно другой вид. Добавим элемент и изменим директиву @ Page, указав другую тему:

<%@ Page Theme="SmokeAndGlass" %>

Новый атрибут Theme директивы @ Page объявляет, что к странице применяется заданная тема. Кроме того, темы можно применять программно через свойство Theme класса Page. Тема (theme) — это набор скинов, а скин (skin) — набор визуальных атрибутов, применяемых к типу элемента управления. BasicBlue и SmokeAndGlass — предопределенные, или глобальные, темы в ASP.NET 2.0.  Их файлы содержатся в подкаталогах каталога Microsoft.NET\Framework\…\ASP.NETClientFiles\Themes.

Вы можете определить собственные темы и скины и развернуть их в подкаталогах каталога Themes вашего приложения. Каждый подкаталог содержит файлы определенной темы, причем имя темы совпадает с именем подкаталога. Подкаталог с темой содержит один или несколько .skin-файлов и другие ресурсы, используемые темой, например, файлы изображений и таблицы стилей. Определения скинов содержатся в .skin-файлах и во многом аналогичны тэгам, применяемым для объявления экземпляров элементов управления в ASPX-файлах.

Чтобы посмотреть, как это работает, создайте подкаталог Themes в папке, где находится DataSource2.aspx. В каталоге Themes создайте подкаталог ShockingPink. В каталоге ShockingPink создайте .skin-файл и поместите в него следующий фрагмент:

<asp:DropDownList runat="server" BackColor="hotpink"
  ForeColor="white" />

<asp:DataGrid runat="server" BackColor="#CCCCCC" BorderWidth="2pt"
  BorderStyle="Solid" BorderColor="#CCCCCC" GridLines="Vertical"
  HorizontalAlign="Left">
  <HeaderStyle ForeColor="white" BackColor="hotpink" />
  <ItemStyle ForeColor="black" BackColor="white" />
  <AlternatingItemStyle BackColor="pink" ForeColor="black" />
</asp:DataGrid>

Затем измените директиву @ Page в файле DataSource2.aspx:

<%@ Page Theme="ShockingPink" %>

В результате получится, несомненно, одна из самых кричащих Web-страниц, которые когда-либо создавались!

ShockingPink.skin задает внешний вид по умолчанию для элементов управления DropDownList и DataGrid. Имена .skin-файлов не обязательно должны совпадать с именами тем, к которым они относятся, хотя часто совпадают. Тема может содержать несколько .skin-файлов, а один .skin-файл может определять атрибуты для любого количества типов элементов управления. Кроме того, в ASP.NET бывают скины по умолчанию и не по умолчанию. Скин без атрибута SkinID по определению является скином по умолчанию. Скины не по умолчанию содержат атрибут SkinID, на который можно ссылаться, указывая SkinID в тэгах элементов управления.

Новые элементы управления

В ASP.NET 2.0 появится около 50 новых типов элементов управления, позволяющих разрабатывать богатый UI, не вникая в тонкости HTML, клиентских сценариев и DOM-модели (Document Object Model) браузера. На врезке «Новые элементы управления, планируемые в ASP.NET 2.0» перечислены элементы управления, которые на момент написания статьи предполагается ввести в ASP.NET 2.0 (в список не включены элементы Web Parts).

Элемент управления DynamicImage упрощает вывод на Web-страницах динамически генерируемых изображений. Раньше разработчикам приходилось писать для динамической генерации изображений собственные HTTP-обработчики или, что хуже, генерировать изображения в ASPX-файлах. Благодаря DynamicImage обе эти методики уходят в прошлое. DynamicImage в коде в листинге 4 используется, чтобы вывести круговую диаграмму. Ключевым является оператор, присваивающий биты изображения массиву ImageBytes элемента управления.

Листинг 4. DynamicImage.aspx

<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.IO" %>

<html>
  <body>
    <asp:DynamicImage ID="PieChart" DynamicImageType="ImageBytes"
       RunAt="server" />
  </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    // Создаем битовую карту и рисуем круговую диаграмму
    Bitmap bitmap = new Bitmap (240, 180, PixelFormat.Format32bppArgb);
    Graphics g = Graphics.FromImage (bitmap);
    DrawPieChart (g, Color.White, new decimal[]
        { 100.0m, 200.0m, 300.0m, 400.0m }, 240, 180);
    g.Dispose();

    // Присоединяем изображение к элементу DynamicImage
    MemoryStream stream = new MemoryStream ();
    bitmap.Save (stream, ImageFormat.Gif);
    bitmap.Dispose();
    PieChart.ImageBytes = stream.ToArray ();
}

void DrawPieChart (Graphics g, Color bkgnd, decimal[] vals,
    int width, int height)
{
    // Очищаем фон
    SolidBrush br = new SolidBrush (bkgnd);
    g.FillRectangle (br, 0, 0, width, height);
    br.Dispose ();

    // Создаем массив кистей
    SolidBrush[] brushes = new SolidBrush[6];
    brushes[0] = new SolidBrush (Color.Red);
    brushes[1] = new SolidBrush (Color.Yellow);
    brushes[2] = new SolidBrush (Color.Blue);
    brushes[3] = new SolidBrush (Color.Cyan);
    brushes[4] = new SolidBrush (Color.Magenta);
    brushes[5] = new SolidBrush (Color.Green);

    // Суммируем входные значения
    decimal total = 0.0m;
    foreach (decimal val in vals)
        total += val;

    // Рисуем диаграмму
    float start = 0.0f;
    float end = 0.0f;
    decimal current = 0.0m;

    for (int i=0; i<vals.Length; i++) {
        current += vals[i];
        start = end;
        end = (float) (current / total) * 360.0f;
        g.FillPie (brushes[i % 6], 0.0f, 0.0f, width, height,
            start, end - start);
    }

    // Очищаем ресурсы и выходим
    foreach (SolidBrush brush in brushes)
        brush.Dispose ();
}
</script>

Элемент управления DynamicImage использует новый сервис ASP.NET 2.0 — сервис генерации изображений. Этот сервис можно применять и при динамическом формировании изображений в ASIX-файлах (еще одно новшество ASP.NET 2.0). В примерах к этой статье (доступных на сайте MSDN Magazine) имеется файл DynamicImage.asix, демонстрирующий основы ASIX-файлов. Чтобы его запустить, скопируйте DynamicImage.asix в виртуальный каталог вашего Web-сервера и откройте в браузере.

Еще один интересный и потенциально очень полезный элемент управления, дебютирующий в ASP.NET 2.0,  — MultiView. Элемент управления MultiView, используемый совместно с элементами управления View, служит для создания страниц с несколькими логическими представлениями. В любой момент показывается только одно представление (с индексом, равным значению свойства ActiveViewIndex элемента MultiView). Вы можете переходить от одного представления к другому, изменяя индекс активного представления. Элементы MultiView идеально подходят для страниц, где есть ярлычки или другие элементы управления, позволяющие выбирать текущую логическую страницу.

В странице в листинге 5 элемент MultiView используется, чтобы показать два разных представления таблицы Titles базы данных Pubs: одно выводится с помощью GridView, другое — с помощью DetailsView. Переключение между представлениями выполняется выбором элемента из раскрывающегося списка. Заметьте: атрибут AllowPaging тэга <asp:DetailsView> позволяет просматривать записи в DetailsView.

Листинг 5. MultiView.aspx

<%@ Page Theme="BasicBlue" %>

<html>
  <body>
    <form runat="server">
      <asp:SqlDataSource ID="Titles" RunAt="server"
        ConnectionString="server=localhost;database=pubs; Integrated
                          Security=SSPI"
        SelectCommand="select title_id, title, price from titles" />
      <asp:DropDownList ID="ViewType" AutoPostBack="true"
        OnSelectedIndexChanged="OnSwitchView" RunAt="server">
        <asp:ListItem Text="GridView" Selected="true" RunAt="server" />
        <asp:ListItem Text="DetailsView" RunAt="server" />
      </asp:DropDownList>
      <asp:MultiView ID="Main" ActiveViewIndex="0" RunAt="server">
        <asp:View RunAt="server">
          <asp:GridView DataSourceID="Titles" RunAt="server" />
        </asp:View>
        <asp:View RunAt="server">
          <asp:DetailsView DataSourceID="Titles" AllowPaging="true"
            RunAt="server" />
        </asp:View>
      </asp:MultiView>
    </form>
  </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    if (IsPostBack)
        DataBind ();
}

void OnSwitchView (Object sender, EventArgs e)
{
    Main.ActiveViewIndex = ViewType.SelectedIndex;
}
</script>

Элементы управления GridView и DetailsView

DataGrid — один из самых популярных элементов управления в ASP.NET, но в некоторых отношениях он — жертва собственного успеха: у него настолько богатая функциональность, что разработчики для ASP.NET хотят еще большего. DataGrid в ASP.NET 2.0 изменился незначительно, зато появилось два новых элемента — GridView и DetailsView, предоставляющие возможности, которых часто требовали от DataGrid.

GridView, как и DataGrid, визуализирует HTML-таблицы, но в отличие от DataGrid может самостоятельно выполнять разбиение на страницы и сортировку. Кроме того, GridView поддерживает больше типов столбцов (типов полей в терминах GridView), чем DataGrid, и более интеллектуально ведет себя при рендеринге, например автоматически отображает логические значения флажками. Не составляет труда объединить GridView с DetailsView, чтобы создать представление «родитель-потомок». Основной недостаток GridView в том, что он, как и DataGrid, выполняет большую часть операций, возвращая форму на сервер.

В листинге 6 показано, как с помощью GridView и DetailsView создать простое представление «родитель-потомок» (master-detail view) таблицы Titles базы данных Pubs. Элементы SqlDataSource предоставляют данные, отображаемые этими элементами управления. SelectParameter элемента SqlDataSource связан с элементом GridView, что позволяет вывести в DetailsView запись, выбранную в GridView. Для выбора записи нужно щелкнуть одну из кнопок Select в элементе GridView. Чтобы такие кнопки появились, в тэг <asp:GridView> помещен атрибут AutoGenerateSelectButton="true».

Листинг 6. MasterDetail.aspx

<%@ Page Theme="BasicBlue" %>

<html>
  <body>
    <form runat="server">
      <asp:SqlDataSource ID="Titles1" RunAt="server"
        ConnectionString="server=localhost;database=pubs;Integrated
          Security=SSPI"
        SelectCommand="select title_id, title, price from titles" />
      <asp:SqlDataSource ID="Titles2" RunAt="server"
        ConnectionString="server=localhost;database=pubs;Integrated
          Security=SSPI"
        SelectCommand="select title_id, title, price from titles where
          title_id=@title_id">
        <SelectParameters>
          <asp:ControlParameter Name="title_id" ControlID="MyGridView"
            PropertyName="SelectedValue" />
        </SelectParameters>
      </asp:SqlDataSource>
      <table><tr><td>
        <asp:GridView ID="MyGridView" DataSourceID="Titles1"
          Width="100%" RunAt="server" AutoGenerateColumns="false"
          SelectedIndex="0" AutoGenerateSelectButton="true"
          DataKeyNames="title_id">
          <Columns>
            <asp:BoundField HeaderText="Title ID"
              DataField="title_id" />
            <asp:BoundField HeaderText="Book Title"
              DataField="title" />
            <asp:BoundField HeaderText="Price" DataField="price"
              DataFormatString="{0:c}" NullDisplayText="TBD" />
          </Columns>
        </asp:GridView>
      </td></tr>
      <tr><td>
        <asp:DetailsView DataSourceID="Titles2" RunAt="server"
          AutoGenerateRows="false" Width="100%">
          <Fields>
            <asp:BoundField HeaderText="Title ID"
              DataField="title_id" />
            <asp:BoundField HeaderText="Book Title"
              DataField="title" />
            <asp:BoundField HeaderText="Price" DataField="price"
              DataFormatString="{0:c}" NullDisplayText="TBD" />
          </Fields>
        </asp:DetailsView>
      </td></tr></table>
    </form>
  </body>
</html>

Заметьте, что элементы <Columns> и <Fields> определяют типы полей элементов управления GridView и DetailsView. Они почти эквивалентны элементам <Columns> в элементах управления DataGrid. Поддерживаемые типы полей перечислены в табл. Особый интерес представляют типы ImageField и DropDownListField, избавляющие разработчиков от необходимости вручную писать код, который выводит в элементах управления DataGrid изображения и раскрывающиеся списки.

Табл. 2. Типы полей GridView и DetailsView
AutoGeneratedField
Тип поля по умолчанию
BoundField
Связывается с заданным полем источника данных
ButtonField
Показывает кнопку, кнопку-изображение или кнопку-ссылку
CheckBoxField
Показывает флажок
CommandField
Показывает кнопки для выбора и редактирования элементов
DropDownListField
Показывает раскрывающийся список
HyperLinkField
Показывает гиперссылку
ImageField
Показывает изображение
TemplateField
Показывает содержимое, определяемое HTML-шаблонами

Новшества в области администрирования

Еще один ощутимый недостаток ASP.NET, исправленный в ASP.NET 2.0, — полное отсутствие декларативных или программных интерфейсов, предназначенных для администрирования Web-сайтов. Раньше для изменения параметров конфигурации приходилось запускать Notepad и редактировать Machine.config или Web.config. С этим покончено. В ASP.NET 2.0 имеется развитой API администрирования, упрощающий чтение и запись параметров конфигурации. Кроме того, есть GUI администрирования, показываемый в браузере при запросе файла Webadmin.axd (рис. 2).

Изображение GIF  Рис. 2. GUI администрирования

На момент написания статьи разработка Webadmin.axd еще не закончена. Он будет служить для конфигурирования различных сервисов, входящих в ASP.NET 2.0 (например сервисов управления членством и ролями), просмотра статистики Web-сайта и настройки параметров защиты.

Сервис управления членством

Одно из лучших новых средств ASP.NET 2.0 — сервис управления членством (membership service), предоставляющий удобный API для создания учетных записей пользователей и управления ими. С появлением ASP.NET 1.x началось массовое применение аутентификации на основе форм, но, чтобы применять такую аутентификацию на практике, по-прежнему приходилось писать довольно много кода. Сервис управления членством устраняет этот недостаток аутентификации на основе форм в ASP.NET 1.x и значительно упрощает написание кода аутентификации на основе форм.

Для работы с Membership API служат два новых класса: Membership и MembershipUser. Первый содержит статические методы для создания пользователей, их проверки и др. MembershipUser представляет отдельных пользователей и содержит методы и свойства для считывания и смены паролей, получения даты последнего входа и т. д. Например, следующий оператор принимает имя и пароль пользователя и возвращает true или false в зависимости от того, допустим ли этот пользователь. Такие операторы заменят вызовы использовавшихся в приложениях ASP.NET 1.x самодельных методов, которые проверяли удостоверения защиты через Active Directory или серверные базы данных:

bool isValid = Membership.ValidateUser (username, password);

Следующий оператор возвращает объект MembershipUser, представляющий пользователя с именем jeffpro:

MembershipUser user = Membership.GetUser ("jeffpro");

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

string email = user.Email;

Где хранятся имена пользователей, пароли и другие данные, c которыми работает сервис управления членством? Как и почти все сервисы ASP.NET 2.0,  управляющие состоянием, этот сервис основан на провайдерах. Провайдеры — модули, позволяющие сервисам взаимодействовать с физическими источниками данных. В ASP.NET 2.0 будут входить провайдеры управления членством для баз данных Microsoft Access, SQL Server, службы каталогов Active Directory и, вероятно, для других источников данных. Вы можете написать собственные провайдеры для каких-либо источников данных. Роб Говард (Rob Howard), менеджер программы в группе Microsoft Web Platform and Tools детально рассмотрел эту тематику в статье «Nothin» But ASP.NET», доступной по ссылке http://msdn.microsoft.com/library/en-us/dnaspnet/html/asp02182004.asp.

По умолчанию сервис управления членством использует провайдер Access и хранит данные о членстве в файле AspNetDB.mdb, находящемся в подкаталоге Data приложения. Можно выбрать другие провайдеры, указав их в разделе <membership> файла Web.config. Редактировать Web.config вручную необязательно, его можно изменить с помощью Webadmin.axd. Следующий фрагмент взят из Web.config после того, как через Webadmin.axd я создал базу данных SQL Server с именем WhidbeyLogin и настроил сервис управления членством на ее использование:

<membership defaultProvider="WhidbeyLogin">
  <providers>
    <add name="WhidbeyLogin"
      type="System.Web.Security.SqlMembershipProvider, ..."
      connectionStringName="webAdminConnection632112624221191376"
      applicationName="/Whidbey" requiresUniqueEmail="false"
      enablePasswordRetrieval="true" enablePasswordReset="false"
      requiresQuestionAndAnswer="false"
      passwordFormat="Encrypted" />
  </providers>
</membership>

Атрибут connectionStringName ссылается на строку подключения, содержащуюся в новом разделе <connectionStrings> файла Web.config. В ASP.NET 2.0 эту часть Web.config можно зашифровать, чтобы защитить строки подключения к базе данных.

Область применения Webadmin.axd не ограничивается созданием баз данных и выбором провайдеров управления членством. Это средство годится для создания пользователей, управления удостоверениями защиты и для других целей. Webadmin.axd и Membership API предоставляют декларативные и программные средства управления зарегистрированными пользователями вашего сайта. Это огромный шаг вперед по сравнению с ASP.NET 1.x,  где проблему управления удостоверениями приходилось решать в основном своими силами.

Элементы управления регистрацией

Сервис управления членством значительно сократил объем кода, необходимого для проверки регистрационных данных и управления пользователями. Но, кроме этого сервиса, введено новое семейство элементов управления, называемых элементами управления регистрацией (login controls), которые еще больше упростили аутентификацию на основе форм. Такие элементы можно использовать как совместно с сервисом управления членством, так и без него. Однако они настолько хорошо интегрируются с этим сервисом, что при совместном использовании сервиса управления членством и элементов управления регистрацией типичные задачи вроде проверки имен и паролей пользователей и отправки забытых паролей по электронной почте можно решать без единой строки кода. Во врезке «Новые элементы управления, планируемые в ASP.NET 2.0»  дан список элементов управления регистрацией, которые предполагается включить в ASP.NET 2.0.

Элемент Login, показанный на рис. , — центральный элемент семейства. Он не только предоставляет гибко настраиваемый UI, но и может вызывать метод Membership.ValidateUser для проверки имени и пароля пользователя. Login также может вызвать метод FormsAuthentication.RedirectFromLoginPage, чтобы перенаправить пользователя на страницу, которую он пытался получить перед тем, как был направлен на страницу входа. Затем FormsAuthentication.RedirectFromLoginPage создает аутенификационные cookie. Позже я покажу, как работают Login и другие элементы управления регистрацией.

Изображение GIF  Рис. 3. Элементы управления регистрацией

Диспетчер ролей

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

Управление ролями опирается на провайдеры и активизируется в Web.config. У диспетчера ролей есть API, реализованный в новом классе Roles, содержащем такие методы, как CreateRole, DeleteRole и AddUserToRole. Важно отметить, что вы можете вообще не вызывать эти методы, поскольку Webadmin.axd полностью поддерживает создание ролей, их назначение пользователям и т. д. Достаточно один раз активизировать защиту на основе ролей, и дальше она «просто работает», используя заданную информацию о ролях и директивы авторизации URL в файлах Web.config, уже знакомые вам по ASP.NET 1.x.

Познакомившись с сервисом управления членством, элементами управления регистрацией и диспетчером ролей, вы, наверное, хотели бы увидеть пример использования этих трех средств. В примеры кода к этой статье, которые вы можете скачать, входит двухстраничное приложение, демонстрирующее аутентификацию на основе форм в стиле Visual Studio 2005. Чтобы развернуть приложение и посмотреть, как оно работает, сначала скопируйте файлы PublicPage.aspx, LoginPage.aspx и Web.config в виртуальный каталог вашего Web-сервера. Создайте в виртуальном каталоге подкаталог Secure и скопируйте в него ProtectedPage.aspx и еще один файл Web.config.

Запустите Webadmin.axd и настройте сайт на поддержку аутентификации на основе форм, сервиса управления членством и диспетчера ролей, выбрав провайдер по своему усмотрению. Создайте пользователей Bob и Alice и роли Manager и Developer. Назначьте пользователю Bob роль Manager, а Alice — роль Developer. (Я не буду перечислять все выполняемые для этого операции, поскольку они скорее всего изменятся еще до того, как вы прочитаете статью. К счастью, интерфейс средства Webadmin.axd вполне понятен интуитивно, и в Webadmin.axd есть мастера, помогающие выполнить настройку.)

Далее откройте PublicPage.aspx в браузере и щелкните кнопку View Secret Message, чтобы посмотреть ProtectedPage.aspx. ASP.NET перенаправит вас на LoginPage.aspx, в которой для запроса имени и пароля пользователя применяется элемент Login. Войдите, указав имя и пароль пользователя Bob. Страница ProtectedPage.aspx откроется в браузере, поскольку Bob имеет роль Manager, а файл Web.config в каталоге Secure разрешает доступ менеджерам. Заметьте: в элементе LoginName показывается имя пользователя, а в элементе LoginStatus — ссылка Log out. Наконец, закройте браузер, снова запустите его и откройте PublicPage.aspx. Щелкните View Secret Message и войдите как Alice. На этот раз вы не сможете открыть ProtectedPage.aspx, так как Alice не является менеджером.

Я использовал аналогичное приложение для обучения аутентификации на основе форм в ASP.NET 1.x,  но для версии 1.x пришлось написать гораздо больше кода. Версия 2.0 заслуживает похвалы за краткость кода — особенно за то, что не нужно писать код проверки удостоверений, введенных в форму входа, или сопоставлять имена пользователей ролям. Если вы до сих пор сомневаетесь, попробуйте написать то же самое приложение в ASP.NET 1.x!  Кроме того, посмотрите изменения, внесенные Webadmin.axd в Web.config. Помимо всего прочего, вы увидите элемент <roleManager>, который активизирует диспетчер ролей и обычно задает провайдер, используемый при управлении ролями.

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

Персонализация

Еще одна новинка — сервис персонализации, предоставляющий готовое решение для хранения персональных параметров, задаваемых посетителями сайта. В настоящее время такие параметры обычно хранят в cookie, в серверных базах данных или и там, и там. Независимо от того, где они хранятся, ASP.NET 1.x мало чем помогает в этом случае. Приходится своими силами создавать и настраивать серверное хранилище этих данных и получать данные персонализации по именам пользователей, прошедших аутентификацию, по cookie или каким-то другим способом.

Сервис персонализации ASP.NET 2.0 облегчает хранение и считывание персональных параметров пользователей. Он основан на профилях пользователей. Профили определяются в Web.config с помощью нового элемента <profile>. Ниже приведен фрагмент файла Web.config:

<profile>
  <properties>
    <add name="Theme" />
    <add name="Birthday" Type="System.DateTime" />
    <add name="LoginCount" Type="System.Int32" defaultValue="0" />
  </properties>
</profile>

В нем определен профиль, содержащий три свойства: Theme строкового типа, Birthday типа DateTime и LoginCount целого типа. Последнее по умолчанию равно 0.

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

string theme = Profile.Theme;
DateTime birthday = Profile.Birthday;
int logins = Profile.LoginCount;

Свойствам профиля можно присваивать значения:

Profile.Theme = "SmokeAndGlass";
Profile.Birthday = new DateTime (1959, 9, 30);
Profile.LoginCount = Profile.LoginCount + 1;

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

Так где же хранятся данные персонализации? Возможны варианты. Сервис персонализации основан на провайдерах, поэтому его можно настроить на использование любого доступного провайдера. В ASP.NET 2.0 войдут минимум два провайдера персонализации: для Access и для SQL Server. Если не указано иное, сервис персонализации использует провайдер для Access, по умолчанию хранящий данные персонализации локально в Data\AspNetDB.mdb. Вместо Access можно задействовать SQL Server, изменив Web.config вручную или через Webadmin.axd. Если вы не хотите хранить данные персонализации ни в Access, ни в SQL Server, пишите свой провайдер.

По умолчанию ASP.NET использует в качестве ключа к данным персонализации имя аутентифицированного пользователя, но можно настроить ASP.NET и на поддержку персонализации для анонимных пользователей. Прежде всего вы должны разрешить анонимную идентификацию, добавив в Web.config:

<anonymousIdentification enabled="true" />

Затем добавьте allowAnonymous="true" в свойства профиля, которые вы хотите хранить для анонимных пользователей:

<name="Theme" allowAnonymous="true" />

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

По умолчанию при анонимной идентификации, чтобы идентифицировать пользователей, повторно посещающих сайт, используются cookie. С помощью атрибутов, указываемых в <anonymousIdentification>, можно различными способами настроить эти cookie. Например, задать имя cookie и указать, надо ли шифровать содержимое cookie. Или настроить сервис персонализации на анонимную аутентификацию без cookie, при которой для идентификации пользователя, повторно посещающего сайт, применяется передача идентификатора сеанса через URL (URL munging). Предусмотрена даже возможность автоматического определения: если выполняющий запрос браузер поддерживает cookie, используются cookie, а в ином случае идентификатор сеанса передается в URL.

Чтобы посмотреть, как работает персонализация, запустите один из примеров кода к статье — Personalize.aspx. Эта страница позволяет каждому посетителю сайта выбрать тему, затем запоминает ее и применяет всякий раз, когда посетитель возвращается на сайт. Заметьте: тема программно применяется к странице в PreInit — новом событии, которое происходит перед Init.

Перед запуском примера разрешите анонимную аутентификацию и определите профиль, содержащий строковое свойство Theme. Следующий фрагмент кода из файла Web.config показывает, как решить эти две задачи:

<configuration>
  <system.web>
    <anonymousIdentification enabled="true" />
    <profile>
      <properties>
        <property name="Theme" allowAnonymous="true" />
      </properties>
    </profile>
  </system.web>
</configuration>

Зависимости кэша от SQL-данных

Еще одна возможность, которой сильно не хватало в ASP.NET 1.x,  — поддержка зависимостей кэша от базы данных. Элементы, помещаемые в кэш приложения ASP.NET, могли зависеть от других кэшированных элементов или объектов файловой системы, но не от записей базы данных. В ASP.NET 2.0 этот недостаток исправлен введением зависимостей кэша от SQL-данных (SQL cache dependencies).

Зависимости кэша от SQL-данных представляются экземплярами нового класса SQLCacheDependency. Применять их — проще некуда. Следующий оператор вставляет DataSet с именем ds в кэш приложения и создает зависимость между DataSet и таблицей Products базы данных Northwind:

Cache.Insert ("ProductsDataSet", ds,
    new SqlCacheDependency ("Northwind", "Products");

Если содержимое таблицы Products изменится, ASP.NET автоматически удалит DataSet.

Зависимости кэша от SQL-данных можно использовать и в кэше вывода ASP.NET. Следующая директива указывает ASP.NET, что вывод страницы, содержащей данные, кэшируется до тех пор, пока не изменится содержимое таблицы Products или пока не пройдет 60 секунд (смотря что случится раньше):

<%@ OutputCache Duration="60" VaryByParam="None"
    SqlDependency="Northwind:Products" %>

Зависимости кэша от SQL-данных можно использовать при работе с SQL Server 7.0,  SQL Server 2000 и с будущей версией SQL Server 2005. SQL Server 2005 будет поддерживать зависимости кэша от SQL-данных без какой-либо предварительной настройки, а SQL Server 7.0 и SQL Server 2000 для поддержки этих зависимостей придется соответствующим образом сконфигурировать. Конфигурирование заключается в создании триггеров базы данных и специальной таблицы, к которой ASP.NET обращается, чтобы определить, внесены ли изменения. Эта таблица периодически опрашивается фоновым потоком через интервал, который задается при настройке и по умолчанию равен пяти секундам. В SQL Server 2005, чтобы определить, внесены ли изменения, не потребуются ни специальная таблица, ни периодический опрос. Кроме того, зависимости кэша от данных SQL Server 2005 можно применять на уровне записей, а зависимости кэша от данных SQL Server 7.0 ;или SQL Server 2000 работают на уровне таблиц. Чтобы настроить базу данных на поддержку зависимостей кэша от SQL-данных, можно воспользоваться утилитой Aspnet_regsqlcache.exe или Webadmin.axd.

Новая модель динамической компиляции

Одно из многих новшеств ASP.NET 1.x заключалось в том, что система могла компилировать код при первом обращении к нему. Однако автоматически компилировались только страницы, а вспомогательные классы, такие как компоненты доступа к данным, приходилось компилировать отдельно.

ASP.NET 2.0 расширяет модель динамической компиляции: теперь практически все можно компилировать автоматически. Каталог bin по-прежнему существует для обратной совместимости, но теперь его дополняют каталоги Code и Resources. Файлы с кодом на C# и Visual Basic в каталоге Code и файлы .resx и .resource в каталоге Resources автоматически компилируются ASP.NET и кэшируются в системных подкаталогах. Более того, WSDL-файлы (Web Services Description Language), скопированные в каталог Code, компилируются в прокси Web-сервисов, а XSD-файлы (XML Schema Definition Language) — в типизированные DataSet. Отредактировав файл Web.config, можно расширить применение этих каталогов, настроив поддержку динамической компиляции для других типов файлов.

Предкомпиляция и развертывание без исходного кода

Когда речь заходит о динамической компиляции, один из наиболее часто задаваемых вопросов по ASP.NET 1.x — можно ли заранее компилировать страницы, чтобы при первом обращении к странице не было задержки из-за затрат времени на компиляцию. Сам по себе вопрос не совсем корректен (задержка минимальна и связанные с ней издержки пренебрежимо малы, если принять во внимание, что потом выполняются тысячи или даже миллионы запросов). Тем не менее Microsoft посчитала своим долгом сделать кое-что, чтобы облегчить жизнь разработчикам. Это «кое-что» — возможность заранее скомпилировать все страницы приложения, отправив запрос фантомному ресурсу precompile.axd.

Но это еще не все, что сделано в области предкомпиляции. Еще одна широко востребованная возможность — способность заранее компилировать все приложения в управляемые сборки, которые допускается развертывать без исходного кода, что особенно удобно при хостинге. В ASP.NET 2.0 введена новая утилита командной строки — Aspnet_compiler.exe, которая выполняет предкомпиляцию и развертывание без исходного кода; в Visual Studio 2005 войдет аналогичное средство. Следующая команда выполняет предкомпиляцию приложения в каталоге Web1 и развертывает его без исходного кода в каталоге Web2:

Aspnet_compiler -v /web1 -p c:\web1 c:\web2

После выполнения команды каталог назначения содержит пустые файлы ASP.NET (ASPX, ASCX, ASIX и т. д.) и копии любого статического содержимого исходного каталога: HTML-файлы, .config-файлы и файлы изображений. Развертывание без исходных текстов не обеспечивает «железную» защиту вашей интеллектуальной собственности (поскольку квалифицированный сотрудник Интернет-провайдера все равно сможет разобраться в вашем приложении, декомпилировав сгенерированные сборки), но значительно поднимает барьер перед обычными взломщиками кода.

Новая модель разделения кода

ASP.NET 1.x поддерживает две программные модели: встраиваемого кода (inline model), в которой HTML и код сосуществуют в одном ASPX-файле, и отделенного (codebehind model), где HTML хранится отдельно в ASPX-файле, а код содержится в файлах исходного кода (например в C#-файлах). В ASP.NET 2.0 вводится третья модель: новая форма отделенного кода, основанная на поддержке частичных классов компиляторами Visual C# и Visual Basic .NET. Новая модель призвана исправить неприятный недостаток исходной модели; он заключался в том, что традиционные классы отделенного кода должны были содержать защищенные поля, чьи имена и типы сопоставляются элементам управления, объявленным в ASPX-файле.

В листингах 710 и 8 показано, как работает новая модель отделенного кода. Hello.aspx содержит декларативную часть страницы, а Hello.aspx.cs — код. Обратите внимание на атрибут CompileWith в директиве @ Page. Также заметьте, что в классе MyPage отсутствуют какие бы то ни было поля, сопоставляемые элементам управления, объявленным в ASPX-файле. Старая модель отделенного кода по-прежнему поддерживается, но в дальнейшем предпочтение будет отдаваться новой модели. Не удивительно, что в Visual Studio 2005 будет встроена поддержка новой модели разделения кода.

Листинг 7. Модель отделенного кода. Файл Hello.aspx

<%@ Page CompileWith="Hello.aspx.cs" ClassName="MyPage" %>

<html>
  <body>
    <form runat="server">
      <asp:TextBox ID="Input" RunAt="server" />
      <asp:Button Text="Test" OnClick="OnTest" RunAt="server" />
      <asp:Label ID="Output" RunAt="server" />
    </form>
  </body>
</html>

Листинг 8. Модель отделенного кода. Файл Hello.aspx.cs

using System;

partial class MyPage
{
    void OnTest (Object sender, EventArgs e)
    {
        Output.Text = "Hello, " + Input.Text;
    }
}

Клиентский диспетчер обратных вызовов

Одна из моих любимых функций ASP.NET 2.0 — «облегченный» возврат формы («lightweight» postback), обеспечиваемый клиентским диспетчером обратных вызовов. Раньше страницы ASP.NET, чтобы вызвать код на стороне сервера, должны были возвратить форму серверу. Возвраты формы неэффективны, так как возвращаются все данные, сгенерированные элементами управления страницы. Кроме того, при возврате формы выполняется актуализация страницы, что вызывает неприятное мигание.

В ASP.NET 2.0 вводится клиентский диспетчер обратных вызовов, позволяющий страницам выполнять обратные вызовы сервера без полного возврата формы. Обратные вызовы асинхронны и выполняются с использованием XML-HTTP. В их данные не включаются возвращаемые данные (postback data), и они не приводят к актуализации страницы. (На серверной стороне до события PreRender страница обрабатывается, как обычно, но затем обработка прекращается, поэтому HTML-данные заново не отображаются.) При этом браузер должен поддерживать протокол XML-HTTP, т. е. нужен Microsoft Internet Explorer версии 5.0 или выше.

Для использования клиентского диспетчера обратных вызовов требуется выполнить три операции. Во-первых, вызовите Page.GetCallbackEventReference, чтобы получить функцию, вызываемую из клиентского сценария для обратного вызова сервера по протоколу XML-HTTP. ASP.NET вернет имя и реализацию этой функции. Во-вторых, напишите метод клиентского сценария, вызываемый, когда обратный вызов возвращает управление. Имя метода — один из аргументов, передаваемых GetCallbackEventReference. В-третьих, реализуйте в странице интерфейс ICallbackEventHandler. В нем содержится единственный метод RaiseCallbackEvent, вызываемый на стороне сервера, когда выполняется обратный вызов. Строка, возвращаемая RaiseCallbackEvent, передается методу, о котором говорилось в описании предыдущей операции.

Код в листинге 9 показывает, как работают клиентские обратные вызовы, и демонстрирует одно из наиболее типичных практических применений таких вызовов. Страница выводит форму, запрашивающую имя и адрес. Введите ZIP-код 378xx или 379xx в поле Zip Code и щелкните кнопку Autofill — в поле City появится название города. Страница обращается к серверу, чтобы получить название города, причем для этого выполняется клиентский обратный вызов, а не полный возврат формы. На практике для преобразования ZIP-кода в название города скорее всего выполнялся бы запрос к базе данных. Заметьте: страница не перерисовывается в отличие от ситуаций, когда используется возврат формы серверу. Информация на странице обновляется быстро и корректно!

Листинг 9. Callback.aspx

<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

<html>
  <body>
    <h1>Please Register</h1>
    <hr>
    <form runat="server">
      <table>
        <tr>
          <td>First Name</td>
          <td><asp:TextBox ID="FirstName" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>Last Name</td>
          <td><asp:TextBox ID="LastName" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>Address 1</td>
          <td><asp:TextBox ID="Address1" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>Address 2</td>
          <td><asp:TextBox ID="Address2" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>City</td>
          <td><asp:TextBox ID="City" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>State</td>
          <td><asp:TextBox ID="State" RunAt="server" /></td>
          <td></td>
        </tr>
        <tr>
          <td>Zip Code</td>
          <td><asp:TextBox ID="Zip" RunAt="server" /></td>
          <td><asp:Button ID="AutofillButton" Text="Autofill"
            RunAt="server" /></td>
        </tr>
      </table>
    </form>
  </body>
</html>

<script language="javascript">
// Функция, вызываемая, когда обратный вызов возвращает управление
function __onCallbackCompleted (result, context)
{
    // Показываем в поле ввода "City" строку,
    // возвращаемую методом RaiseCallbackEvent сервера
    document.getElementById ('City').value = result
}
</script>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    // Получаем код события обратного вызова
    // (например "__doCallback (...)")
    string cbref = GetCallbackEventReference (this,
        "document.getElementById ('Zip').value",
        "__onCallbackCompleted", "null", "null");

    // Связываем событие обратного вызова с кнопкой Autofill
    // через атрибут onclick (и добавляем "return false" в код
    // события, чтобы не было возврата формы)
    AutofillButton.Attributes.Add ("onclick",
        cbref + "; return false;");
}

// Обработчик события обратного вызова на серверной стороне
string ICallbackEventHandler.RaiseCallbackEvent (string arg)
{
    if (arg.StartsWith ("378"))
        return "Oak Ridge";
    else if (arg.StartsWith ("379"))
        return "Knoxville";
    else
        return "Unknown";
}
</script>

Группы верификации

Элементы управления «верификатор» (validation controls) — одно из самых замечательных новшеств ASP.NET 1.x.  Такие элементы, как RequiredFieldValidator и RegularExpressionValidator, позволяют разработчиком выполнять и на клиенте, и на сервере интеллектуальную проверку ввода, причем для этого не нужно быть экспертом по клиентским сценариям и DOM браузера. К сожалению, у элементов управления «верификатор» в версии 1.x есть один серьезный недостаток. Нет удобного способа сгруппировать их, чтобы верификаторы в одной части страницы могли переопределять верификаторы в другой части страницы и разрешать возврат формы независимо от состояния верификаторов в другой части страницы.

Иллюстрацией к этой проблеме является страница ValidationGroups1.aspx, входящая в скачиваемые примеры к статье. Разработчик страницы хотел бы, чтобы пользователи заполняли одну из групп элементов TextBox и возвращали форму на сервер, не заполняя другую группу, но этого не происходит. Если заполнены не все поля, один из верификаторов выведет сообщение об ошибке (рис. 4).

Изображение GIF  Рис. 4. Элементы управления «верификатор» в ASP.NET 1.x

Группы верификации (validation groups) — новое средство ASP.NET 2.0,  которое решит эту проблему раз и навсегда. Элементы управления «верификатор» теперь можно группировать с помощью свойства ValidationGroup. Это же свойство используется, чтобы сопоставлять кнопки группам. Если ввод удовлетворяет требованиям всех верификаторов группы и возврат формы инициируется элементом управления, относящимся к той же группе верификации, возврат формы разрешается. Пример применения этой методики — страница ValidationGroups2.aspx (листинг ). Внешне она идентична ValidationGroups1.aspx, но внутренне значительно отличается от предыдущей страницы. Вы можете заполнить любую из групп элементов TextBox и вернуть форму, щелкнув кнопку, относящуюся к той же группе верификации, что и заполненные элементы TextBox.

Листинг 10. ValidationGroups2.aspx

<html>
  <body>
    <form runat="server">
      <h1>New Users</h1>
      <table>
        <tr>
          <td>User Name</td>
          <td><asp:TextBox ID="NewUserName" RunAt="server" /></td>
          <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
            ControlToValidate="NewUserName" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td>Password</td>
          <td><asp:TextBox ID="NewPassword1" TextMode="Password"
            RunAt="server" /></td>
          <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
            ControlToValidate="NewPassword1" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td>Retype Password</td>
          <td><asp:TextBox ID="NewPassword2" TextMode="Password"
            RunAt="server" /></td>
          <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
            ControlToValidate="NewPassword2" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td>E-Mail Address</td>
          <td><asp:TextBox ID="NewEMail" RunAt="server" /></td>
          <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
            ControlToValidate="NewEMail" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td></td>
          <td><asp:Button ValidationGroup="NewUsers"
            Text="Create Account" OnClick="OnCreateAccount"
            RunAt="server" /></td>
          <td></td>
        </tr>
      </table>
      <hr>
      <h1>Existing Users</h1>
      <table>
        <tr>
          <td>User Name</td>
          <td><asp:TextBox ID="UserName" RunAt="server" /></td>
          <td><asp:RequiredFieldValidator
            ValidationGroup="ExistingUsers"
            ControlToValidate="UserName" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td>Password</td>
          <td><asp:TextBox ID="Password" TextMode="Password"
            RunAt="server" /></td>
          <td><asp:RequiredFieldValidator
            ValidationGroup="ExistingUsers"
            ControlToValidate="Password" ErrorMessage="Required"
            RunAt="server" /></td>
        </tr>
        <tr>
          <td></td>
          <td><asp:Button ValidationGroup="ExistingUsers"
            Text="Log In" OnClick="OnLogIn" RunAt="server" /></td>
          <td></td>
        </tr>
      </table>
    </form>
  </body>
</html>

<script language="C#" runat="server">
  void OnCreateAccount (Object sender, EventArgs e) {}
  void OnLogIn (Object sender, EventArgs e) {}
</script>

Возврат формы для другой страницы

Разработчики для ASP.NET 1.x часто жаловались на то, что страницы могут возвращать форму только для самих себя. В версии 2.0 ситуация изменится благодаря введению возврата формы для другой страницы (cross-page posting). Чтобы форма возвращалась для другой страницы, нужно указать страницу-адресат в свойстве PostBackUrl элемента управления, инициирующего возврат формы. Это показано в следующем коде файла PageOne.aspx:

<html>
  <body>
    <form runat="server">
      <asp:TextBox ID="Input" RunAt="server" />
      <asp:Button Text="Test" PostBackUrl="PageTwo.aspx"
        RunAt="server" />
    </form>
  </body>
</html>
По щелчку кнопки PageOne.aspx возвращает форму для PageTwo.aspx:
<html>
  <body>
    <asp:Label ID="Output" RunAt="server" />
  </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    TextBox input = (TextBox) PreviousPage.FindControl ("Input");
    Output.Text = "Hello, " + input.Text;
}
</script>

В PageTwo.aspx, чтобы получить ссылку на исходную страницу, используется новое свойство PreviousPage класса Page. Простой вызов FindControl возвращает ссылку на TextBox, объявленный в PageOne.aspx, что позволяет получить данные, введенные пользователем.

По умолчанию System.Web.UI.Page.PreviousPage возвращает слабо типизированную ссылку на страницу, инициировавшую возврат формы. Однако, если PageOne.aspx — единственная страница, способная возвратить форму странице PageTwo.aspx, то PageTwo.aspx может с помощью новой директивы @ PreviousPageType получить строго типизированный доступ к PageOne.aspx, что продемонстрировано в следующем примере кода:

<%@ PreviousPageType TypeName="ASP.PageOne.aspx" %>

  ...
Output.Text = "Hello, " + PreviousPage.Input.Text;

Заключение

В ASP.NET 2.0 много других новых средств, не рассмотренных в статье. Например, встроенный сервис ведения статистики сайта (site-counter service) позволяет вести статистику использования сайта и просматривать ее в Webadmin.axd или в GUI собственной разработки. Новая подсистема Web Parts предоставляет инфраструктуру для разработки порталов в стиле SharePoint Server. Интегрированная поддержка мобильных устройств означает, что больше не придется устанавливать отдельный инструментальный набор, чтобы адаптировать вывод к PDA и другим устройствам с небольшим объемом памяти. Внесена масса усовершенствований в существующие элементы управления, благодаря чему они стали более гибкими средствами разработки Web-страниц, основанных на компонентах.

Пора уже сейчас приступать к ознакомлению с ASP.NET 2.0:  зная, что появится в новой версии (и что не появится), вы сможете уже сегодня планировать архитектуру, на которую будет легко перейти завтра. Ваши приложения для ASP.NET 1.x должны работать в версии 2.0 без изменений, поскольку Microsoft пообещала, что обеспечит совместимость со старой платформой. Но будущее принадлежит ASP.NET 2.0,  и в этом будущем вы сможете получить более мощную функциональность с меньшими усилиями. Неужели вы этому не рады?



Опубликовал admin
19 Окт, Вторник 2004г.



Программирование для чайников.