В ASP.NET 2.0 введен ряд новых средств, улучшающих доступ к данным, в частности несколько элементов управления — источников данных (далее для краткости элементов источников данных) и элементов, связываемых с данными. Благодаря новым элементам источников данных исчезает необходимость в написании огромного объема однотипного кода, без которого не обойтись в ASP.NET 1.x. Например, не составляет труда создать элементы источников данных для SQL-операторов или хранимых процедур и сопоставить с ними элементы, связываемые с данными. Еще больше впечатляет то, что элемент ObjectDataSource позволяет упростить разработку и уменьшить число строк кода, в то же время абстрагируясь от бизнес-логики и логики доступа к данным, реализованной на различных уровнях n-уровневой архитектуры.
До появления .NET при разработке сеток с данными часто приходилось писать большое количество кода, который «на лету» формирует HTML-таблицу, перебирая в цикле содержимое ADO-элемента Recordset. В ASP.NET 1.x стало проще разрабатывать такие сетки, поскольку появилась возможность связывания объекта DataSet, представляющего XML-данные, с ASP.NET-элементом DataGrid. Однако и в традиционной ASP, и в ASP.NET 1.x приходится писать код, реализующий разбиение на страницы, сортировку, редактирование и выбор строк. Благодаря усовершенствованиям в ASP.NET 2.0 число строк кода, необходимого для получения сетки, заполненной данными и полностью поддерживающей разбиение на страницы, сортировку и редактирование, резко уменьшилось.
Сегодня я сначала покажу, насколько просто разработать Web-приложение для ASP.NET 2.0, использующее SqlDataSource и некоторые новые элементы управления, связываемые с данными. Замечу, что я работал с версией Beta 1.
Большинство корпоративных приложений основано на многоуровневых архитектурах, в которых промежуточный уровень содержит бизнес-логику, а уровень доступа к данным работает с одной или несколькими базами данных, хранящимися на сервере. Я расскажу об элементе ObjectDataSource, идеальном для интеграции с существующими многоуровневыми компонентами. Связывая элемент управления ObjectDataSource с бизнес-объектами, можно задействовать существующую многоуровневую архитектуру и значительно сократить объем кода, генерирующего сложный Web UI. Кроме того, у элемента ObjectDataSource есть некоторые особые свойства, позволяющие связывать его со строго типизированными DataSet и компонентами доступа к данным ASP.NET 2.0 и ADO.NET 2.0. К другим новым возможностям и усовершенствованиям, реализованным в ASP.NET 2.0, относятся выражения двухстороннего связывания (two-way binding expressions), улучшенное кэширование и несколько элементов ASP.NET 2.0, которые можно связывать с новыми элементами источников данных.
Элементы управления,связываемые с данными
Чтобы использовать элементы источников данных, нужно сопоставить с ними какие-либо элементы, связываемые с данными. В ASP.NET 2.0 введено несколько новых элементов, связываемых с данными: GridView, DetailsView и FormView. Если вы обожаете элемент управления DataGrid из ASP.NET 1.x, вам понравится и элемент GridView из ASP.NET 2.0. GridView — нечто вроде DataGrid на стероидах: он поддерживает новые элементы источников данных, а также с его помощью можно реализовать сортировку, редактирование и разбиение на страницы, написав гораздо меньше кода, чем в случае DataGrid (подробнее о GridView см. в статье Дино Эспозито (Dino Esposito) в «MSDN Magazine» за август 2004 г. по ссылке msdn.microsoft.com/msdnmag/issues/04/08/GridView).
Чтобы связать GridView с элементом источника данных, присвойте свойству DataSourceID элемента GridView идентификатор элемента источника данных. Кроме того, у GridView имеется несколько свойств, которые можно задать для улучшения внешнего вида элемента и взаимодействия с пользователем (в дальнейшем я рассмотрю соответствующие примеры):
DataSourceID=»sdsOrdersDataSource»
AutoGenerateColumns=»True»>
Другие элементы управления, такие как DropDownList, также можно связывать с элементами источников данных. Например, DropDownList можно связать с элементом SqlDataSource, получающим список сотрудников. В DropDownList можно показывать ФИО сотрудника, а поле EmployeeID использовать при связывании с данными в качестве поля, содержащего значение. В следующем примере определен DropDownList, который будет отображать список имен клиентов, позволяющий выбрать определенного клиента. Этот элемент связан с SqlDataSource-элементом sdsCustomerDataSource, получающим список полей CompanyName и CustomerID из таблицы клиентов:
AutoPostBack=»True»
DataSourceID=»sdsCustomersDataSource»
DataTextField=»CompanyName»
DataValueField=»CustomerID»>
В ASP.NET 2.0 связать элемент управления с элементом источника данных весьма легко. Для этого необязательно писать какой бы то ни было отделенный код (codebehind). Однако при желании можно написать код, явно связывающий элементы управления с данными. Кроме того, как и в ASP.NET 1.x, вам доступны свойства DataSource и DataMember элементов управления, связываемых с данными.
Элементы источников данных
В ASP.NET 2.0 введено несколько новых элементов источников данных: SqlDataSource, ObjectDataSource, XmlDataSource, AccessDataSource и SiteMapDataSource (табл. 1). Все эти элементы можно применять для получения данных из источников соответствующих типов и сопоставлять с различными элементами управления, связываемыми с данными. Элементы источников данных уменьшают число строк кода, которые приходится писать, чтобы получить данные и связать с ними элементы управления (и даже чтобы реализовать сортировку данных, их разбиение на страницы и редактирование).
Все элементы источников данных имеют аналогичные свойства, позволяющие им взаимодействовать с соответствующими источниками данных. SiteMapDataSource и XmlDataSource предназначены для получения иерархических данных, а остальные элементы источников данных — для считывания наборов данных, состоящих из полей и записей.
Элемент AccessDataSource разработан специально для получения данных из баз данных Access. По названию SqlDataSource создается впечатление, что он работает только с SQL Server, но это не так. На самом деле он используется для выборки данных из любого источника, совместимого с OLE DB или ODBC.
Типы и параметры команд
У элемента SqlDataSource четыре свойства-команды, с помощью которых можно задать, как SqlDataSource получает, вставляет, обновляет и удаляет данные. Свойство SelectCommand может содержать SQL-оператор или имя хранимой процедуры. И в том, и в другом случае при необходимости можно передать параметры. Свойства InsertCommand, UpdateCommand и DeleteCommand указывают SqlDataSource, какие SQL-операторы (или хранимые процедуры) выполняются для модификации данных, содержащихся в соответствующей базе данных. В примере показан SqlDataSource, у которого свойства SelectCommand и UpdateCommand содержат параметризованные SQL-запросы. Заметьте: элемент UpdateParameters включает подэлементы Parameter, указывающие имена и типы полей, для которых определены параметры. Если связать с этим SqlDataSource элемент управления GridView, значения параметров, описанных элементом UpdateParameters, будут сопоставлены с одноименными полями редактируемой записи.
Самый простой способ опробовать этот элемент в деле — создать Web-форму в Visual Studio 2005, в окне Server Explorer связать ее с базой данных Northwind локального SQL Server и перетащить таблицу на Web-форму. При этом автоматически будут созданы элементы SqlDataSource и GridView. Visual Studio сам присвоит свойствам ProviderName и ConnectionString элемента управления SqlDataSource значения, нужные для соединения с базой данных Northwind SQL Server, а всем четырем свойствам-командам — соответствующие SQL-операторы. Чтобы Web-форма поддерживала редактирование, достаточно раскрыть смарт-тэг и установить флажок Enable Editing.
В элементах источников данных можно использовать и параметры, задаваемые в других элементах управления. Например, элемент источника данных должен получать информацию обо всех заказах данного клиента. В этом случае параметр CustomerID можно передавать в SQL-выражение или хранимую процедуру, заданную в свойстве SelectCommand элемента SqlDataSource. Это позволило бы получать значение параметра CustomerID из другого элемента управления, например DropDownList, и напрямую передавать его в SQL-команду SelectCommand элемента SqlDataSource.
Вы можете напрямую связать данные элемента управления с параметром любого из SQL-операторов элемента SqlDataSource (SelectCommand, InsertCommand, UpdateCommand или DeleteCommand). Кроме того, можно задать, какое именно свойство элемента управления должно использоваться в качестве параметра. Так, если в предыдущем примере вы хотите передавать в качестве параметра свойство DataTextField, а не свойство элемента DropDownList, используемое по умолчанию, то в элементе ControlParameter укажите, что значение свойства PropertyName равно DataTextField.
Помимо ControlParameter, в элементах источников данных применяются параметры и других типов. ProfileParameter служит для получения значения параметра из объекта профиля (если вы используете новые возможности персонализации, доступные в ASP.NET). Существует несколько типов параметров, значения которых считываются из наборов, содержащихся в стандартном объекте Request. Например, CookieParameter позволяет получить значение параметра из cookie. Значение параметра QueryStringParameter считывается из переменной, содержащей строку запроса, а значение FormParameter — из поля ввода HTML-формы. Наконец, значение параметра SessionParameter извлекается из переменной сеанса. С помощью параметров этих типов можно различными способами задавать значения параметров элементов источников данных.
Пример SqlDataSource
Итак, я познакомил вас с элементами источников данных, теперь сравним чтение и изменение данных с применением SqlDataSource и ObjectDataSource. Элемент SqlDataSource использует объект ADO.NET 2.0 DbProviderFactory и имеет свойства, позволяющие напрямую связать его с источником данных OLE DB или ODBC. При загрузке страницы ASP.NET, содержащей элемент, связанный с данными SqlDataSource, элемент SqlDataSource напрямую взаимодействует с соответствующей базой данных. Таким образом, элемент источника данных SqlDataSource не поддерживает интеграцию с существующими бизнес-объектами.
Чтобы увидеть SqlDataSource в действии, посмотрим страницу Orders_SDS.aspx (рис. 3). Она содержит элемент управления DropDownList, который связан с элементом SqlDataSource, считывающим список клиентов из базы данных Northwind.
Здесь есть еще один SqlDataSource — элемент sdsOrdersDataSource, считывающий данные обо всех заказах выбранного клиента. В коде взятом из файла Orders_SDS.aspx (его можно скачать с сайта MSDN Magazine), определены два элемента управления SqlDataSource и элемент DropDownList. Заметьте: элемент SqlDataSource sdsOrdersDataSource использует значение, выбранное в DropDownList, передавая его в качестве параметра хранимой процедуре prGet_Orders.
Свойства ProviderName и ConnectionString указывают элементам SqlDataSource, из какого хранилища данных получать информацию. Благодаря этим свойствам SqlDataSource является удобным средством считывания и изменения данных, но, к сожалению, строки подключения к базе данных и SQL-операторы или имена хранимых процедур оказываются доступными в ASPX-файлах презентационного уровня. Как правило, это не очень хорошо. Гораздо безопаснее хранить эти данные в зашифрованном виде в репозитарии конфигурационной информации (например в конфигурационном файле или в реестре).
На рис. 3 показана страница Orders_SDS.aspx в режиме редактирования. Элемент GridView автоматически реализует разбиение на страницы: его свойство AllowPaging имеет значение true, а свойство PageSize задает требуемый размер страницы, например 10. Это означает, что сетка должна разбивать записи на страницы и автоматически загружать сетку и страницу заново при щелчке ссылки на предыдущую или следующую страницу. GridView показывает поля выбранной записи, которые можно редактировать, как элементы TextBox. Столбцы, связываемые с данными, задаются элементами asp:BoundField или TemplateField. При использовании BoundField для связывания столбца элемента GridView с полем источника данных задается свойство DataField:
SortExpression=»ShipCity»>
Этот элемент указывает GridView, что в режиме просмотра значение поля ShipCity показывается в элементе span. Если GridView находится в режиме редактирования, то в выбранной строке это поле также показывается соответствующим HTML-элементом. В данном случае таким HTML-элементом будет TextBox, поскольку это строковое поле. Элемент выбирается в зависимости от типа данных поля, связанного со столбцом. Например, если поле имеет тип bit (SQL Server), для отображения поля в режиме редактирования будет задействован элемент управления CheckBox.
Элемент TemplateField позволяет более гибко управлять поведением столбца сетки, связанного с данными. Рассмотрим следующий фрагмент кода страницы Orders_SDS.aspx:
Элемент TemplateField используется, чтобы показывать поле OrderDate в элементе управления Label, когда GridView находится в режиме просмотра, и в элементе
TextBox, когда GridView находится в режиме редактирования. Кроме того, TemplateField позволяет с помощью элемента FooterTemplate задать, как выводится поле в нижней строке сетки. Можно также определить подэлементы HeaderTemplate, AlternatingItemTemplate и даже InsertItemTemplate. Обратите внимание на сокращенный синтаксис связывания, введенный в ASP.NET 2.0. Значение OrderDate задается вызовом выражения Bind, которому передаются имя поля источника данных, связанного с GridView, и необязательное строковое выражение, определяющее формат данных. В данном случае указывается строка формата, представляющая дату в краткой форме. Все эти параметры несложно задать через свойства, доступные через смарт-тэг — новое средство Visual Studio 2005.
ObjectDataSource
Одна из самых приятных возможностей GridView и других элементов управления, связываемых с данными, заключается в том, что после создания их можно сопоставить с элементом ObjectDataSource или SqlDataSource, изменив всего одно свойство. Так, вы можете создать элемент управления ObjectDataSource и изменить свойство DataSourceID элемента GridView на идентификатор этого нового элемента ObjectDataSource.
В отличие от SqlDataSource элемент ObjectDataSource позволяет абстрагироваться от параметров ASPX-страницы и презентационного уровня, специфичных для базы данных, и перенести эту специфику на более низкие уровни многоуровневой архитектуры. Например, в отличие от SqlDataSource у элемента ObjectDataSource нет свойств ConnectionString, ProviderName и SelectCommand, зато есть другие свойства, указывающие, экземпляр какого бизнес-класса создается и какой метод используется для получения или изменения данных.
Чтобы настроить, как элемент ObjectDataSource будет обращаться к бизнес-классу другого уровня и его методам, сначала настройте свойство TypeName элемента ObjectDataSource (например Type-Name=»MSDN2005Jan_BLL.Orders»). Затем укажите в свойстве SelectMethod имя метода бизнес-класса, который будет использоваться для получения данных этого источника. Метод бизнес-класса должен возвращать перечислимый список, такой как набор, массив DataSet или DataReader. Чтобы все это работало, ObjectDataSource должен иметь возможность выполнить заданный метод. Если это статический метод, ничего делать не нужно. Если это метод экземпляра, ObjectDataSource должен быть способным создать экземпляр класса. Самый простой способ обеспечить это — написать бизнес-класс с конструктором по умолчанию. Еще один вариант — обрабатывать событие ObjectCreating элемента ObjectDataSource, в котором можно создать экземпляр объекта с каким угодно конструктором, а затем передать экземпляр объекта элементу источника данных.
В примере кода (взятом из файла Orders.aspx, который также можно скачать с сайта MSDN Maga-zine) показаны два объекта ObjectDataSource, замещающие два объекта SqlDataSource, показанные в предыдущем примере. Элемент odsOrdersDataSource использует класс MSDN2005Jan_BLL.Orders и его метод GetData, чтобы получить список заказов. В этом примере метод GetData класса Orders просто создает экземпляр строго типизированного DataSet с именем OrdersDataSet и связанный с ним адаптер OrdersTableAdapter. Затем он вызывает метод Fill и возвращает строго типизированный DataSet:
public OrdersDataSet GetData(string CustomerID)
{
OrdersDataSet oDs = new OrdersDataSet();
OrdersTableAdapter oDa = new OrdersTableAdapter();
oDa.Fill(oDs, CustomerID);
return oDs;
}
Заметьте: метод GetData принимает параметр Custo-merID, описываемый элементом SelectParameters определения ObjectDataSource. Метод UpdateData также принимает параметры, описываемые элементом Update-Parameters (рис. 7). Ниже показана сигнатура метода UpdateData класса Orders:
public void UpdateData(int OrderID, DateTime OrderDate,
string ShipCity, string ShipCountry)
Имена и типы параметров метода UpdateData должны соответствовать именам и типов параметров обновления, заданных в элементе ObjectDataSource. Элементы источников данных не поддерживают пакетные обновления, поэтому нельзя передать методу обновления сразу несколько записей. Вместо этого каждое значение передается методу обновления как отдельный параметр. Помимо свойств SelectMethod и UpdateMethod, у элемента ObjectDataSource есть свойства DeleteMethod и InsertMethod.
Усовершенствованныйстрого типизированный DataSet
В предыдущем примере было показано, как связать GridView с элементом ObjectDataSource, который в свою очередь связан с классом бизнес-уровня, методы которого используются для считывания и обновления данных. Этот пример отлично работает, если у вас уже имеются уровень бизнес-логики и многоуровневая архитектура. Кроме того, таким образом можно вызывать методы клиентского прокси Web-сервиса или любого другого класса, на который есть ссылка и который соответствует требованиям к классу и ме-тодам.
Один из аспектов предыдущего примера, который я сознательно не рассматривал до настоящего времени, — роль строго типизированного DataSet. С помощью мастеров Visual Studio 2005 можно определить методы выборки, вставки, обновления и удаления данных прямо в типизированном DataSet. То есть вообще не писать никакой ADO.NET-код для на бизнес-уровне или уровне доступа к данным, а добавить логику для ADO.NET прямо в типизированныйDataSet.
Вы не обязаны использовать типизированный DataSet, но он может оказаться полезным в определенных случаях, поскольку в него введен ряд усовершенствований. При создании типизированного DataSet по умолчанию создается метод Fill, который добавляется в класс TableAdapter, содержащийся в определении типизированного DataSet. В этом дополнительном классе TableAdapter можно хранить строку подключения, а также имена хранимых процедур или SQL-операторы, используемые для выборки, обновления, вставки и удаления записей базы данных.
Типизированный DataSet также позволяет создавать собственные методы считывания и изменения данных. DataSet Orders — это типизированный DataSet, созданный для хранимой процедуры prGet_Orders. С помощью мастера настройки компонентов доступа к данным (data component queryconfiguration wizard) я добавил в класс OrdersTable-Adapter два собственных метода: GetData и UpdateData. Эти методы определены в файле класса, связанном с XSD типизированного DataSet; в данном случае это файл OrdersDataSet.Designer.cs. Если вы заинте ресовались, откройте этот автоматически сгенерированный файл (но не изменяйте его, так как ваши изменения будут перезаписаны, если файл будет сгенерирован заново) и посмотрите методы GetData и Update, а также весь стандартный код, созданный для типизированного DataSet. Если вы запустите пример страницы Orders2.aspx, она установит прямую связь с этими нестандартными методами. Все это может значительно сократить объем кода, который приходится писать при реализации промежуточныхуровней.
Еще пару словоб элементах источников данных
Итак, мы подошли к концу. Возможно, вас заинтересует, что элементы источников данных поддерживают кэширование. Для этого в них есть ряд свойств. Если присвоить свойству EnableCaching значение true и задать в свойстве CacheDuration число секунд, данные будут храниться в кэше в течение этого периода. Свойству CacheExpirationPolicy можно присвоить значение Absolute или Sliding. Политика Absolute применяется по умолчанию. Она означает, что отсчет времени начинается с момента загрузки кэша. При использовании политики Sliding счетчик времени сбрасывается при каждом обращении к кэшированным данным. Эти методики кэширования позволяют оптимизировать приложения в случаях, когда загрузка данных осуществляется с более-менее постоянной интенсивностью. Например, было бы очень эффективно использовать кэширование в источнике данных, который загружает в DropDownList названия государств, городов и даже категорий товаров, поскольку эти данные изменяются не так уж часто.
Элемент ObjectDataSource обертывает код, вызывающий бизнес-методы бизнес-объектов. Он работает совместно с элементами, которые связываются с данными (вроде элемента GridView) и которые поддерживают разбиение на страницы, сортировку и редактирование данных. В ASP.NET 1.x все это приходилось программировать вручную.
Заключение
Усовершенствования, внесенные в ASP.NET 2.0, — особенно в части, касающиеся элементов источников данных и элементов, связываемых с данными, — значительно сокращают объем кода, нужного для создания сетки, заполненной данными и полностью поддерживающей разбиение на страницы, сортировку и редактирование. Хотя элементы источников данных избавляют от написания огромного объема кода, вы можете по-прежнему писать собственный код взаимодействия с элементами источников данных. Вы можете не только создать Web-страницу для отображения данных, просто щелкая мышью в нужных местах, но и написать код, обрабатывающий события элементов источников данных, такие как Selected, Selecting, Updated или Updating.
Исходный код можно скачать по ссылке: http://msdn.microsoft.com/msdnmag/code05.aspx.