Доступ к данным с помощью элементов управления источником данных (DataSource) в ASP.NET 2.0
ОГЛАВЛЕНИЕ
В дополнение к возможности декларативного использования элементов управления источником данных, доступ к ним также может быть осуществлен программным путем. Вы можете добавить элемент управления SqlDataSource либо AccessDataSource на вашу веб-страницу, указать запрос типа SELECT, INSERT, UPDATE или DELETE, и затем программно выполнить одну из SQL-команд элемента. Такой подход пригоден в случаях, когда вам необходимо программно работать с данными, но в то же время обойтись без написания кода. Вкратце, использование элементов управления источником данных таким образом позволяет программно работать с данными, написав лишь одну строку кода.
В данной статье мы рассмотрим способ использования элемента управления SqlDataSource для того, чтобы программно получать, вставлять и удалять информацию. Читайте далее, чтобы узнать больше об этом!
Программный запуск SQL-команд с элементами управления SqlDataSource и AccessDataSource
Элементы управления SqlDataSource и AccessDataSource могут быть использованы для того, чтобы осуществлять вставку, обновление, удаление и получение данных. SQL-команды, используемые для осуществления вставки, обновления, удаления и возврата данных, указаны посредством свойств InsertCommand, UpdateCommand, DeleteCommand и SelectCommand соответственно, и данные команды выполняются автоматически, когда элемент управления источником данных декларативно привязывается к элементу управления данными (такими, как GridView, DetailsView, DropDownList и т.д.).
SQL-команды, указанные в данных свойствах могут содержать параметры. К примеру, свойство SelectCommand может содержать запрос SELECT, который возвращает все товары определенной категории используя параметр под названием @CategoryID like so:
SELECT ProductID, ProductName, UnitPrice
FROM Products
WHERE Category = @CategoryID
Значение параметра может быть указано посредством элемента управления Parameter в наборе SelectParameters элемента управления источником данных. Элемент управления источником данных может использовать параметризированные запросы для всех своих SQL-команд (InsertCommand, UpdateCommand, DeleteCommand и SelectCommand), каждая из которых указана соответствующими свойствами наборов параметров (InsertParameters, UpdateParameters, DeleteParameters и SelectParameters соответственно ).
Элементы управления SqlDataSource и AccessDataSource предоставляют четыре метода, которые позволяют выполнение конкретной SQL-команды. Вот эти четыре метода:
- Select(DataSourceSelectArguments) - выполняет SelectCommand и получает результаты либо в виде объекта DataView, либо как DataReader, в зависимости от значения свойства DataSourceMode элемента управления источником данных. Объект DataSourceSelectArguments , ожидаемый данным методом, используется для того, чтобы предоставить дополнительную информацию по тому, как информация должна быть обновлена до того, как будет возвращена. К примеру, при сортировке либо листании данных в GridView данный входной параметр используется для указания того, что данные отсортированы по определенной колонке либо то, что только определенное подмножество записей должно быть получено. В примерах данной статьи мы будем всегда передавать значение DataSourceSelectArguments.Empty, тем самым отображая то, что мы не хотим изменять данные.
- Insert() - выполняет InsertCommand.
- Update() - выполняет UpdateCommand.
- Delete() - выполняет DeleteCommand.
Остальная часть данной статьи рассматривает два примера, которые демонстрируют программный способ работы с данными, используя элемент управления SqlDataSource для получения и вставки данных из базы данных Northwind. Полноценный исходный код данных примеров, а также и пример, рассматривающий удаление данных, доступны в конце данной статьи.
Получаем результаты программным способом, используя элемент управления SqlDataSource
Для того чтобы программно получить результат из элемента управления источником данных, нам нужно сначала добавить элемент управления источником на страницу и указать его свойства. Чтобы продемонстрировать программную работу с данными, мы создадим страницу, которая позволит пользователю выбрать категорию товара из таблицы Categories. Кроме выбора категории, представьте, что мы хотим отобразить среднюю цену для товаров в данной категории, а также три самых дорогих товара (название и цену). Хотя мы можем выполнить все эти требования декларативно, используя такие элементы управления данными, как Repeater или FormView, мы все же будем использовать программный способ получения данной информации и отображения ее в элементе Label.
Следующий пример включает элемент DropDownList, названный Categories , который декларативно привязан к SqlDataSource, возвращающему все категории. Страница также содержит два других элемента SqlDataSource, которые не привязаны ни к одному элементу управления. Первый, AvgPriceDataSource, возвращает среднее значение UnitPrice для указанной категории. Его SelectCommand было назначено следующему запросу:
SELECT AVG(UnitPrice) as AvgPrice
FROM Products
WHERE CategoryID = @CategoryID
Второй элемент управления, MostExpensiveProductsDataSource, возвращает значения ProductName и UnitPrice для трех самых дорогих товаров указанной категории, используя следующую SelectCommand команду:
SELECT TOP 3 ProductName, UnitPrice
FROM Products
WHERE CategoryID = @CategoryID
ORDER BY UnitPrice DESC
Оба элемента управления имеют единственный ControlParameter в их наборе SelectParameters, который заполняет значение параметра @CategoryID значением CategoryID , выбранного из элемента управления DropDownList, который назван Categories.
При программной работе с информацией, полученной из элемента управления SqlDataSource, стоит отметить свойство DataSourceMode. Свойство DataSourceMode указывает на тип объекта данных, возвращенного элементом SqlDataSource и оно может быть установлено в DataReader либо DataSet (по умолчанию). Если свойство DataSourceMode установлено в DataReader , то метод Select возвращает экземпляр объекта IDataReader; если оно установлено в DataSet , то возвращается объект DataView.
Разница немаловажна, поскольку код, который вы используете для программной работы с данными отличается между DataViews и Data Readers. Оба объекта могут работать со скалярными данными либо набором данных. Data Readers более эффективны при возврате данных, которые вам просто необходимо отобразить; DataViews предоставляет больше возможностей - возможность сортировать и фильтровать полученные данные, осуществлять беспорядочный доступ к записям, и т.д. AvgPriceDataSource SqlDataSource демонстрирует работу с Data Readers, в то время как MostExpensiveProductsDataSource показывает работу с DataViews.
Следующий метод вызывается в момент, когда изменяется индекс выборки элемента DropDownList и когда данные впервые привязывается к DropDownList. Как демонстрирует код, осуществляется программный вызов метода Select() элементов управления источником данных AvgPriceDataSource и MostExpensiveProductsDataSource и свойствам Text двух разных элементов Label назначается результирующая информация. Заметьте как синтаксис доступа к данным отличается в зависимости от того, что возвращает SqlDataSource - Data Reader или DataView.
Private Sub ProgrammaticallyBindToDataSourceControls()
'Получаем среднюю цену DataReader
Dim myReader As IDataReader = CType(AvgPriceDataSource.Select(DataSourceSelectArguments.Empty), IDataReader)
If myReader.Read Then
If Convert.IsDBNull(myReader("AvgPrice")) Then
AvgPrice.Text = "Unknown"
Else
AvgPrice.Text = Convert.ToDecimal(myReader("AvgPrice")).ToString("c")
End If
End If
'Получаем три самых дорогих товара в качестве DataView
MostExpensiveProducts.Text = String.Empty
Dim vwExpensiveItems As DataView = CType(MostExpensiveProductsDataSource.Select(DataSourceSelectArguments.Empty), DataView)
'Проходим в цикле через каждую запись
For Each rowProduct As DataRowView In vwExpensiveItems
'В результате получаем название и цену
MostExpensiveProducts.Text &= String.Format("{0} ({1:c}); ", rowProduct("ProductName"), rowProduct("UnitPrice"))
Next
End Sub
Код, возвращающий данные из источника данных AvgPriceDataSource использует объект IDataReader. Он затем считывает данные используя метод Read. Поскольку UnitPrice может содержать пустые значения (NULL), может случиться и так, что все товары в выбранной категории имеют значение UnitPrice равное NULL, и в этом случае средняя цена также будет нулевой (NULL). Если такой случай будет реальностью, то мы отобразим "Unknown" (Неизвестно), в противном случае мы отобразим результат в формате валюты. Для того, чтобы получить три самых дорогих товара выбранной категории мы используем объект DataView и проходим в цикле по всех результирующим записям при помощи выражения For Each. Для каждой записи мы отобразим название и цену товара.

Вставка новой записи программным путем, используя элемент управления SqlDataSource
Элементы управления ASP.NET 2.0 предлагают возможности декларативной вставки, обновления и удаления данных. Не написав и строки кода, вы можете создать GridView, DetailsView или FormView, который отображает данные, а также позволяет пользователю редактировать, удалять и добавлять информацию. Тем не менее, бывают случаи, когда данный декларативный подход, не подразумевающий написания кода, не будет тем, что вам необходимо. Конечно, всегда есть возможность написать самому код соединения к базе данных, написать выражение INSERT, UPDATE либо DELETE, и выполнить команду, но это все можно с легкостью сделать при помощи элементов управления источниками данных.
Представьте, что нам нужно создать интерфейс, позволяющий добавлять новые товары в таблицу Products. В частности, мы хотели бы иметь настраиваемый интерфейс, который, на данный момент, отображал бы текстовые поля для названий и цен на товары, DropDownList - для категории, и CheckBox - для состояния. Данный интерфейс также включал бы кнопку добавления ("Add Product"), при нажатии на которую будет добавлена новая запись в базу данных, используя значения, введенные пользователем.
Для того чтобы создать такую страницу без использования FormView либо какого-нибудь другого из элементов управления данными, вам необходимо начать с добавления элементов TextBox, DropDownList, CheckBox и Button. Далее, добавьте элемент управления SqlDataSource, который указывает на следующее параметризированное выражение INSERT посредством его свойства InsertCommand:
INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued)
VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)
Установите соответствие между значениями параметров в наборе InsertParameters при помощи ControlParameters, которые указывают на соответствующие элементы управления страницы. Существует множество путей выполнения данного. Находясь в режиме дизайнера (Designer), нажмите на SqlDataSource и перейдите к окну свойств (Properties). Там вы увидите опцию InsertQuery, которая будучи отмеченной, отображает редактор команд и параметров (Command and Parameter Editor), как это продемонстрировано ниже. Здесь вы можете указать InsertCommand, параметры, и их источники. Заметьте, что каждый из четырех параметров использует элемент управления (Control) в качестве источника (Parameter source), при этом ControlID выпадающего списка установлен в соответствующий элемент управления на странице.

В качестве альтернативы данные параметры могут быть указаны посредством декларативного синтаксиса элемента управления SqlDataSource:
<asp:SqlDataSource ID="AddProductDataSource" runat="server" ConnectionString="..."
InsertCommand="INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued) VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)"
ProviderName="...">
<InsertParameters>
<asp:ControlParameter ControlID="ProductName" Name="ProductName" PropertyName="Text" />
<asp:ControlParameter ControlID="Categories" Name="CategoryID" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="UnitPrice" Name="UnitPrice" PropertyName="Text" />
<asp:ControlParameter ControlID="Discontinued" Name="Discontinued" PropertyName="Checked" />
</InsertParameters>
</asp:SqlDataSource>
В любом событии, как только элементы управления будут добавлены на страницу InsertCommand и InsertParameters элемента SqlDataSource будут правильно настроены, вставка новой записи будет столь же проста, как вызов метода Insert() элемента управления источником данных. Единственным кодом, который вам придется написать, будет эта строка (которую мы расположили в обработчике события Click кнопки "Add Product" ):
Protected Sub btnAddProduct_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddProduct.Click
'Добавляем новый товар
AddProductDataSource.Insert()
End Sub
Вставка информации при помощи элементов управления данными
Элементы управления AccessDataSource, SqlDataSource и ObjectDataSource все поддерживают вставку, обновление и удаления. Вкратце, все три элемента обладают методом Insert, вызов которого выполняет следующую последовательность действий:
- Выполняется событие Inserting источника данных
- Происходит действие вставки
- Выполняется событие Inserted источника данных
Для различных элементов управления данными действие вставки различно. Для элементов AccessDataSource и SqlDataSource данное действие подразумевает соединение с указанной базой данных и выполнением выражения INSERT, указанного свойством InsertCommand. Для ObjectDataSource действие вставки подразумевает создание экземпляра объекта источника данных и вызова указанного InsertMethod. Данная статья фокусируется на элементе управления SqlDataSource.
Давайте детально исследуем действие вставки для элементов управления AccessDataSource и SqlDataSource. Выражение INSERT либо хранимая процедура, указанные командой InsertCommand элемента, используют параметризированный запрос. То есть, если вы используете произвольное SQL-выражение для InsertCommand, то выражение INSERT будет использовать параметры следующим образом:
INSERT INTO TableName (ColumnName1, ColumnName2, ..., ColumnNameN)
VALUES (@Parameter1, @Parameter2, ..., @ParameterN)
В статье о фильтрации базы данных при помощи параметров мы рассмотрели способ использования параметров в SelectCommand для фильтрации результатов, к примеру SELECT * FROM Products WHERE Price < @UnitPrice. Значение параметра @UnitPrice в данном экземпляре указывается посредством SelectParameters элемента управления источником данных, которое также может указать источник для значения параметра. Источником может быть: жёстко запрограммированное значение, например "3.95", которое позволит получить все товары, стоящие меньше чем $3.95; значение элемента управления на странице позволяет пользователю ввести значение цены в TextBox; значение из строки запроса; значение из состояния сессии и т.д.
Аналогично значения параметра в выражении INSERT назначаются основываясь на параметрах в наборе InsertParameters элемента управления источником данных и эти параметры могут использовать те же источники, что и the SelectParameters.
Элементы управления AccessDataSource и SqlDataSource "за кадром" используют стандартные классы ADO.NET для реализации доступа к данным. То есть они соединяются к базе данных при помощи объекта SqlConnection или OleDbConnection и указывают текст команды и параметры посредством объекта SqlCommand или OleDbCommand.
На данном этапе мы можем выразить процесс вставки для AccessDataSource и SqlDataSource более детальным способом:
- Выполняется событие Inserting источника данных
- Создаются объекты SqlConnection и SqlCommand (или OleDbConnection и OleDbCommand)
- Свойству CommandText командного объекта назначается свойство InsertCommand элемента управления источником данных
- К параметрам в наборе InsertParameters элемента управления источником данных добавляется набор Parameters командного объекта
- Устанавливается соединение с базой данных и выполняется команда, тем самым осуществляя вставку данных
- Выполняется событие Inserted источника данных
Остальная часть статьи рассматривает три случая вставки используя элемент управления SqlDataSource:
- вставка данных при помощи формы, созданной вручную
- вставка данных при помощи элемента управления DetailsView
- вставка и возврат значения колонки IDENTITY только что вставленной записи
Вставка данных при помощи формы, созданной вручную
Примеры, доступные в конце данной статьи, демонстрируют различные техники вставки данных в таблицу Products базы данных Northwind. Таблица Products содержит несколько колонок. Каждая запись товара определена уникальным ProductID, который является колонкой типа AutoNumber/IDENTITY. При вставке записи в таблицу единственными двумя колонками, которые необходимы, являются ProductName и Discontinued; все другие колонки допускают нулевое значение (NULL).
Представьте, что нам необходимо создать страницу, которая позволяла бы пользователям добавлять новые записи в таблицу Products посредством указания названия, категории, цены и статуса скидки. Мы смогли создать простую форму, которая включает в себя TextBoxes, один DropDownList и элемент CheckBox для сбора входной информации полей, а также элемент управления Button названный "Add Product", при нажатии на который в базу данных будет вставлена запись нового товара.
В дополнение к данным элементам управления пользовательского ввода мы также можем добавить элемент управления SqlDataSource для обработки вставки. Мы можем настроить InsertCommand элемента в следующее выражение INSERT:
INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued)
VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)
Заметьте, что мы использовали параметры после ключевого слова VALUES. Источниками для значений этих параметров являются элементами управления пользовательского ввода на странице. Установите соответствие между значениями данных параметров в наборе InsertParameters при помощи ControlParameters, которые указывают на соответствующие элементы управления на странице. Существует несколько путей реализации этого. Находясь в режиме дизайнера (Designer) нажмите на SqlDataSource и перейдите к окну свойств (Properties). Там вы увидите опцию InsertQuery, которая будучи отмеченной, отображает редактор команд и параметров (Command and Parameter Editor), как это продемонстрировано ниже. Здесь вы можете указать InsertCommand, параметры, и их источники. Заметьте, что каждый из четырех параметров использует элемент управления (Control) в качестве источника (Parameter source), при этом ControlID выпадающего списка установлен в соответствующий элемент управления на странице.

В качестве альтернативы данные параметры могут быть указаны посредством декларативного синтаксиса элемента управления SqlDataSource:
<asp:SqlDataSource ID="AddProductDataSource" runat="server" ConnectionString="..."
InsertCommand="INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued) VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)"
ProviderName="...">
<InsertParameters>
<asp:ControlParameter ControlID="ProductName" Name="ProductName" PropertyName="Text" />
<asp:ControlParameter ControlID="Categories" Name="CategoryID" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="UnitPrice" Name="UnitPrice" PropertyName="Text" />
<asp:ControlParameter ControlID="Discontinued" Name="Discontinued" PropertyName="Checked" />
</InsertParameters>
</asp:SqlDataSource>
В любом событии, как только элементы управления будут добавлены на страницу InsertCommand и InsertParameters элемента SqlDataSource будут правильно настроены, вставка новой записи будет настолько же проста, как и вызов метода Insert() элемента управления источником данных. Единственным кодом, который вам придется написать, будет эта строка (которую мы расположили в обработчике события Click кнопки "Add Product" ):
Protected Sub btnAddProduct_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddProduct.Click
'Добавляем новый товар
AddProductDataSource.Insert()
End Sub

Вставка данных при помощи элемента управления DetailsView
В ASP.NET 2.0 было представлено несколько новых элементов управления данными, к примеру, GridView, DetailsView, FormView и др. Элементы управления DetailsView и FormView отображают информацию о единственной записи (в отличие от GridView, который отображает информацию о множестве записей). Элементы DetailsView и FormView также можно настроить на отображение интерфейса вставки. Вкратце, вы можете также использовать элементы управления DetailsView или FormView для того, чтобы создать интерфейс вставки информации в базу данных, при этом, не написав и строки кода!
Элемент управления DetailsView против FormView |
---|
Элементы управления DetailsView и FormView имеют много общего - они оба отображают по одной записи и могут отображать интерфейс для вставки и обновления данных. Отличие между двумя элементами заключается в том, что элемент DetailsView составлен из DataFields (BoundFields, CheckBoxFields, TemplateFields и т.д.), также, как и GridView. В результате этого вы увидите приземистый квадратный вид элемента. С другой стороны, FormView использует шаблоны вместо DataFields; следовательно, у вас есть возможность использовать более гибкую разметку для интерфейсов отображения, вставки и обновления. |
Начните с добавления элемента управления SqlDataSource на страницу и используйте тот же InsertCommand, что и в предыдущем примере:
INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued)
VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)
Далее, добавьте параметры к набору InsertParameters элемента. Вместо того чтобы использовать ControlParameters, используйте стандартный объект Parameter. Также DetailsView, который мы создадим для данного примера, не будет включать в себя интерфейс для указания категории. Поэтому установите соответствующее DefaultValue объекта Parameter в "1". Это назначит все товары, добавленные посредством данной страницы, принадлежат категории напитков.
<asp:SqlDataSource ID="AddProductDataSource" runat="server" ConnectionString="..."
InsertCommand="INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued) VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)"
ProviderName="...">
<InsertParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="CategoryID" DefaultValue="1" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
</InsertParameters>
</asp:SqlDataSource>
Далее, добавьте DetailsView на страницу и установите его свойство DataSourceID в значение ID элемента управления SqlDataSource (AddProductDataSource). Из смарт-тега DetailsView отметьте кнопку "Enable Inserting". Это добавит CommandField к DetailsView и установит его свойство ShowInsertButton в значение True. CommandField, при данной настройке, отображает кнопку "New", когда элемент DetailsView находится в режиме ReadOnly. При нажатии на кнопку "New" вызывается постбэк и DetailsView переходит в режим Insert, тем самым заставляя CommandField отображать кнопки "Insert" и "Cancel".
Элемент управления SqlDataSource не содержит значения для своего SelectCommand, так что в DetailsView ничего не будет отображено. На самом деле, для данного примера мы хотим, чтобы DetailsView всегда был в режиме вставки (чтобы пользователю не приходилось всякий раз нажимать кнопку"New" для добавления новой записи). Установите свойство DefaultMode элемента DetailsView в Insert , тем самым указав то, что DetailsView должен отображать только свой интерфейс вставки
Далее, добавьте два BoundFields и CheckBoxField к DetailsView, установив свойства HeaderText и DataField таким образом, чтобы они были привязаны к колонкам ProductName, UnitPrice и Discontinued , используемым элементом SqlDataSource. Наконец, установите свойство AutoGenerateRows в False.
Вы можете сделать это из диалогового окна полей (Fields) либо вручную ввести декларативную разметку элемента. Для того чтобы использовать диалоговое окно Fields, нажмите на ссылку Edit Fields из смарт-тега DetailsView. Добавьте два BoundFields и CheckBoxField и установите их свойства в списке справа. Чтобы установить свойство AutoGenerateRows в False, просто отключите "Auto-generate fields" в нижнем левом углу.

В качестве альтернативы вы можете указать поля DetailsView и установить свойство AutoGenerateRows в False посредством следующего декларативного синтаксиса:
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataSourceID="AddProductDataSource"
DefaultMode="Insert" CellPadding="4" ForeColor="#333333" GridLines="None">
<Fields>
<asp:BoundField DataField="ProductName" HeaderText="Product Name:" />
<asp:BoundField DataField="UnitPrice" HeaderText="Unit Price:" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued:" />
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
И это все! Когда пользователь посещает страницу и вводит название, цену и статус скидки товара и нажимает Insert - вызывается постбэк. DetailsView автоматически присвоит значения элементов управления ввода соответствующим параметрам вставки (InsertParameters) элемента SqlDataSource до того, как начнется цикл вставки. Результатом будет вставка новой записи в базу данных, причем без написания и единой строчки кода, а также без необходимости сопоставления вручную InsertParameters элемента SqlDataSource с их источниками (поскольку DetailsView делает это автоматически - в момент, когда нажимают кнопку Insert).

Данный пример демонстрирует только простые возможности DetailsView и не включает в себя важные моменты, такие как валидация ввода и специализация интерфейса вставки. К примеру, поскольку требуется наличие значения в колонке ProductName, вставка не будет выполнена в случае, если пользователь не заполнит данное текстовое поле. Аналогично в случае, если пользователь введет неправильное значение цены (к примеру "expensive!"), то произойдет ошибка при попытке вставить значение в базу данных. Приложение к данной статье включает в себя другой пример DetailsView, который демонстрирует добавление элементов управления валидацией, а также настраивает интерфейс вставки таким образом, чтобы пользователь мог выбрать категорию для нового товара из выпадающего списка существующих категорий.
Вставка и возврат значения колонки IDENTITY только что вставленной записи
Многие таблицы баз данных предоставляют некоторый механизм для уникальной идентификации каждой записи. Существует множество подходов, но одним из часто применяемых является использование колонки IDENTITY, которая также называется AutoNumber. Значение колонки IDENTITY автоматически присваивается системой базы данных в момент, когда в таблицу добавляется новая запись. Эти значения начинаются с какого-то начального значения (обычно 1) и инкрементируются на какое-то указанное число с добавлением каждой новой записи (обычно увеличивается на 1). Поэтому если вы добавите три новые записи к таблице, значения колонки IDENTITY для данных трех записей будут, соответственно, 1, 2 и 3.
При использовании колонок IDENTITY стандартным требованием могла бы стать возможность получения значения колонки IDENTITY только что вставленной записи. Скорее всего, после вставки новой записи, вам понадобится автоматически перенести пользователя на странице с деталями, куда вы должны передать значение колонки IDENTITY посредством строки запроса, либо вам понадобится добавить дополнительные записи в дочернюю таблицу, и вам потребуется значение колонки IDENTITY только что вставленной записи родительской таблицы для того, чтобы связать записи дочерней таблицы с родительскими. В обоих случаях, в Microsoft SQL Server вы можете использовать ключевое слово SCOPE_IDENTITY() для того, чтобы получить значение колонки IDENTITY только что вставленной записи.
Для того чтобы получить данную информацию при использовании SqlDataSource необходимо сделать следующее:
- Создайте хранимую процедуру, которая возвращала бы значение колонки IDENTITY только что вставленной записи при помощи параметра OUTPUT.
- Настройте SqlDataSource таким образом, чтобы он использовал хранимую процедуру. Это подразумевает обновление InsertCommand в названии хранимой процедуры, созданной в первом пункте, путем установки InsertCommandType элемента управления источником данных в StoredProcedure и добавления выходного параметра к набору InsertParameters.
- Для получения доступа к результирующему значению выходного параметра нам необходимо создать обработчик для события Inserted элемента SqlDataSource. Помните, что данное событие выполняется после того, как действие вставки было выполнено. Как только мы получим значение IDENTITY только что вставленной записи, мы можем использовать его при необходимости.
В приложении к данной статье база данных содержит хранимую процедуру, названную AddProductAndReturnNewProductIDValue, которая принимает четыре входных параметра и имеет один выходной параметр (@NewProductID). Как демонстрирует следующий синтаксис T-SQL, данная хранимая процедура вставляет новую запись в Products и затем присваивает значение, возвращенное SCOPE_IDENTITY(), к @NewProductID:
ALTER PROCEDURE dbo.AddProductAndReturnNewProductIDValue (
@ProductName nvarchar(40),
@CategoryID int,
@UnitPrice money,
@Discontinued bit,
@NewProductID int OUTPUT
)
AS
-- Вставка записи в базу данных
INSERT INTO Products(ProductName, CategoryID, UnitPrice, Discontinued)
VALUES (@ProductName, @CategoryID, @UnitPrice, @Discontinued)
-- Считывание только что вставленного ProductID в @NewProductID
SET @NewProductID = SCOPE_IDENTITY()
Далее, обновите SqlDataSource таким образом, чтобы он использовал AddProductAndReturnNewProductIDValue в качестве InsertCommand вместо незапрограммированного SQL-выражения. Также, добавьте выходной параметр в набор InsertParameters. Заметьте, что выходной параметр в наборе InsertParameters является объектом Parameter , чье свойство Direction установлено в Output:
<asp:SqlDataSource ID="AddProductDataSource" runat="server" ConnectionString="..."
InsertCommand="AddProductAndReturnNewProductIDValue"
ProviderName="..." InsertCommandType="StoredProcedure">
<InsertParameters>
<asp:ControlParameter ControlID="ProductName" Name="ProductName" PropertyName="Text" />
<asp:ControlParameter ControlID="Categories" Name="CategoryID" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="UnitPrice" Name="UnitPrice" PropertyName="Text" />
<asp:ControlParameter ControlID="Discontinued" Name="Discontinued" PropertyName="Checked" />
<asp:Parameter Direction="Output" Name="NewProductID" Type="Int32" />
</InsertParameters>
</asp:SqlDataSource>
Добавление выходного параметра к набору InsertParameters элемента SqlDataSource также добавляет выходной параметр к набору Parameters внутреннего объекта SqlCommand , используемого элементом управления источником данных во время действия вставки. Значение данного параметра может быть исследовано в обработчике события Inserted. Как показывает следующий код обработчика события, внутренний объект SqlCommand доступен через свойство e.Command в обработчике события. Здесь мы можем получить конкретный экземпляр параметра и исследовать его свойство Value для определения значения колонки IDENTITY только что вставленной записи:
Protected Sub AddProductDataSource_Inserted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceStatusEventArgs) Handles AddProductDataSource.Inserted
'Считываем значение выходного параметра @NewProductID
Dim newProductID As Integer = Convert.ToInt32(e.Command.Parameters("@NewProductID").Value)
'Отображаем сообщение о подтверждении
NewProductAddedMsg.Text = String.Format("Товар {0} был добавлен в базу данных ... Значение ProductID данного товара равно {1}...", ProductName.Text, newProductID)
End Sub

Удаление информации при помощи элементов управления данными
Элементы управления AccessDataSource, SqlDataSource и ObjectDataSource все поддерживают вставку, обновление и удаления. Вкратце, все три элемента обладают методом Delete, вызов которого выполняет следующую последовательность действий:
- Выполняется событие Deleting источника данных
- Происходит действие удаления
- Выполняется событие Deleting источника данных
Для различных элементов управления данными действие удаления различно. Для элементов AccessDataSource и SqlDataSource данное действие подразумевает соединение с указанной базой данных и выполнением выражения DELETE, указанного свойством DeleteCommand. Для ObjectDataSource действие удаления подразумевает создание экземпляра объекта источника данных и вызова указанного DeleteMethod. Данная статья фокусируется на элементе управления SqlDataSource.
Давайте детально исследуем действие удаления для элементов управления AccessDataSource и SqlDataSource. Выражение DELETE либо хранимая процедура, указанные DeleteCommand элемента, используют параметризированный запрос. То есть, если вы используете произвольное SQL-выражение для DeleteCommand, то выражение DELETE будет использовать параметры следующим образом:
DELETE FROM TableName
WHERE ColumnName1 = @Parameter1
Многие нестандартные выражения DELETE выглядят также, как и указанное выше: они указывают название таблицы и имеют единственный набор в их условии WHERE. Условие WHERE указывает значение конкретного первичного ключа, который надо удалить. В данном случае, где таблица имеет композитный первичный ключ (т.е., первичный ключ составлен из нескольких колонок), условие WHERE будет сравнивать каждую ключевую колонку следующим образом:
DELETE FROM TableName
WHERE ColumnName1 = @Parameter1 AND ColumnName2 = @Parameter2 AND ... AND ColumnNameN = @ParameterN
В статье о фильтрации базы данных при помощи параметров мы рассмотрели способ использования параметров в SelectCommand для фильтрации результатов, к примеру SELECT * FROM Products WHERE Price < @UnitPrice. Значение параметра @UnitPrice в данном экземпляре указывается посредством SelectParameters элемента управления источником данных, которое также может указать источник для значения параметра. Источником может быть: жёстко запрограммированное значение, например "3.95", которое позволит получить все товары, стоящие меньше чем $3.95; значение элемента управления на странице позволяет пользователю ввести значение цены в TextBox; значение из строки запроса; значение из состояния сессии и т.д.
Аналогично значения параметра в выражении DELETE назначаются основываясь на параметрах в наборе DeleteParameters элемента управления источником данных и эти параметры могут использовать те же источники, что и the SelectParameters.
Элементы управления AccessDataSource и SqlDataSource "за кадром" используют стандартные классы ADO.NET для реализации доступа к данным. То есть они соединяются к базе данных при помощи объекта SqlConnection или OleDbConnection и указывают текст команды и параметры посредством объекта SqlCommand или OleDbCommand.
На данном этапе мы можем выразить процесс удаления для AccessDataSource и SqlDataSource более детальным способом:
- Выполняется событие Deleting источника данных
- Создаются объекты SqlConnection и SqlCommand (или OleDbConnection и OleDbCommand)
- Свойству CommandText командного объекта назначается свойство DeleteCommand элемента управления источником данных
- К параметрам в наборе DeleteParameters элемента управления источником данных добавляется набор Parameters командного объекта
- Устанавливается соединение с базой данных и выполняется команда, тем самым осуществляя удаление данных
- Выполняется событие Deleted источника данных
Остальная часть статьи рассматривает три случая удаления используя элемент управления SqlDataSource: удаление данных вручную программно вызвав метод Delete элемента SqlDataSource; удаление данных при помощи элемента управления GridView; программная остановка процесса удаления в обработчике события Deleting элемента SqlDataSource. Полный код примеров вы можете скачать в конце данной статьи.
Удаление данных вручную
Примеры, доступные в конце данной статьи, демонстрируют различные техники удаления данных из таблицы Products базы данных Northwind. Представьте, что нам необходимо создать страницу, которая позволяла бы пользователям удалять записи из таблицы Products посредством нажатия кнопки Delete. Мы смогли создать простую форму, которая включает в себя DropDownList и элемент Button.
В дополнение к данным элементам управления пользовательского ввода мы также можем добавить элемент управления SqlDataSource для обработки удаления. Этого можно достич при помощи двух элементов управления SqlDataSource - одного для выбора и второго для удаления - либо использовать один элемент управления для обоих случаев. Я использовал один элемент управления.
Предыдущие статьи данной серии исследовали возврат и привязку данных к элементу управления, поэтому мы не рассмотрим шаги привязки списка товаров к DropDownList. Как только вы настроите элемент SqlDataSource на то, чтобы он возвращал записи из таблицы Products и связал их с DropDownList, вы можете настроить свойства SqlDataSource, связанные с удалением, путем выбора элемента управления SqlDataSource в Designer и выбрав DeleteQuery в окне свойств (Properties). Тем самым вы вызовите диалоговое окно, где вы можете ввести свойство DeleteCommand элемента SqlDataSource и указать значения для DeleteParameters.
Поскольку мы хотим удалить конкретную запись из таблицы Products, установите свойство DeleteCommand в следующее выражение DELETE:
DELETE FROM [Products] WHERE [ProductID] = @ProductID
Заметьте, что мы использовали параметр в выражении WHERE. Источником для значения данного параметра будет выбранное значение ProductID в элементе DropDownList. Вам необходимо сопоставить значения параметров со значением свойства SelectedValue в DropDownList посредством набора DeleteParameters используя ControlParameter. Легче всего сделать это из диалогового окна, вызванного нажатием на свойство DeleteQuery элемента SqlDataSource (смотрите следующее изображение).

В качестве альтернативы данные параметры могут быть указаны посредством декларативного синтаксиса элемента управления SqlDataSource:
<asp:SqlDataSource ID="ProductsDataSourceForDropDownList" runat="server" ConnectionString="..."
DeleteCommand="DELETE FROM [Products] WHERE [ProductID] = @ProductID"
SelectCommand="SELECT [ProductID], [ProductName] FROM [Products] ORDER BY [ProductName]">
<DeleteParameters>
<asp:ControlParameter ControlID="ProductList" Name="ProductID" PropertyName="SelectedValue"
Type="Int32" />
</DeleteParameters>
</asp:SqlDataSource>
Заметьте, что указанная декларативная разметка содержит как и настройки свойств относительно удаления (DeleteCommand и DeleteParameters), так и свойство SelectCommand.
В любом событии, как только элементы управления будут добавлены на страницу InsertCommand и InsertParameters элемента SqlDataSource будут правильно настроены, удаление записи можно будет выполнить настолько же просто, как и вызов метода Delete() элемента управления источником данных. Вдобавок к удалению товара нам также необходимо заново осуществить привязку данных к DropDownList (поэтому запись больше не будет нигде фигурировать). Также мы отобразим сообщение, подтверждающее удаление. Это можно выполнить используя обработчик события Click для кнопки "Delete Product".
Protected Sub btnDeleteProduct_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDeleteProduct.Click
'Программно вызываем метод Delete
ProductsDataSourceForDropDownList.Delete()
'Отображаем подтверждающее сообщение
Dim productJustDeleted As String = ProductList.SelectedItem.Text
ClientScript.RegisterStartupScript(Me.GetType(), "Message", String.Format("alert('Товар {0} был удален...');", productJustDeleted.Replace("'", "\'")), True)
'Заново привязываем данные к DropDownList, поэтому только что удаленный товар больше не появится в списках
ProductList.DataBind()
End Sub
Удаление при помощи элемента управления GridView
В ASP.NET 2.0 было представлено несколько новых элементов управления данными, к примеру, GridView, DetailsView, FormView и др. Элемент управления GridView разработан для того, чтобы отображать информацию о множестве записей, а также он обладает функциональностью удаления, которую можно активировать, установив соответствующую галочку. Для того чтобы продемонстрировать данную функциональность, мы рассмотрим случай, когда GridView будет отображать все записи, и затем добавим колонку кнопок удаления к сетке.
Для начала, добавьте элемент управления SqlDataSource на страницу и затем используйте тот же DeleteCommand что и в предыдущем примере:
DELETE FROM [Products] WHERE [ProductID] = @ProductID
Далее добавьте параметр @ProductID к набору DeleteParameters элемента. Вместо того, чтобы использовать ControlParameter , как мы делали это в предыдущем примере, используйте стандартный объект Parameter.
<asp:SqlDataSource ID="ProductsDataSource" runat="server" ConnectionString="..."
DeleteCommand="DELETE FROM [Products] WHERE [ProductID] = @ProductID"
SelectCommand="SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued] FROM [Products] ORDER BY [ProductName]">
<DeleteParameters>
<asp:Parameter Name="ProductID" Type="Int32" />
</DeleteParameters>
</asp:SqlDataSource>
Заметьте: SelectCommand , который мы использовали в упомянутом SqlDataSource немного отличается от предыдущего примера тем, что он возвращает два дополнительных поля из таблицы Products: UnitPrice и Discontinued.
Далее, добавляем на страницу GridView и из его смарт-тега привязываем его к элементу SqlDataSource, который мы только что добавили (ProductsDataSource). При этом BoundFields автоматически добавляется к колонкам, возвращенным в SelectCommand. Он также устанавливает свойство DataKeys элемента GridView в колонки первичного ключа возвращенных данных - в данном случае ">ProductID".
Для того чтобы добавить колонку кнопок удаления (Delete), перейдите к смарт-тегу GridView и активируйте опцию удаления "Enable Deleting". Это добавляет CommandField к DetailsView и устанавливает его свойство ShowDeleteButton в True. CommandField при данной конфигурации отображает колонку, где для каждой строки есть кнопка удаления. Также активируйте опцию листания "Enable Paging", которая также расположена в смарт-теге GridView.
После того, как вы выполните все шаги, декларативная разметка вашего GridView должна быть похожа на следующую:
<asp:GridView ID="gvProducts" runat="server" AllowPaging="True"
AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" SortExpression="Discontinued" />
</Columns>
</asp:GridView>
И это все! При нажатии кнопки Delete выполняются следующие действия:
- Выполняется постбэк
- Выполняется событие RowDeleting элемента GridView
- GridView заполняет @ProductID используя значение DataKeys для того кортежа, где была нажата кнопка удаления (Delete). (Свойство DataKeys должно быть установлено в "ProductID". Если вы очистите данное значение, или оно будет сброшено либо потеряно каким-то образом, то значение параметра @ProductID не будет присвоено при нажатии кнопки Delete. Следовательно, выражение DELETE не удалит ни одной записи! Поэтому, если вы тестируете GridView и узнаете, что кнопка Delete ничего не делает, то первым делом вам необходимо проверить правильно ли настроено свойство DataKeys элемента GridView.)
- GridView вызывает метод Delete() своего SqlDataSource
- Выполняется событие RowDeleted элемента GridView
Данный пример демонстрирует только простые возможности GridView и не включает в себя некоторые возможности, такие как предоставление окна сообщений для подтверждения удаления.

Отмена удаления
Как уже обсуждалось, когда вызывается метод Delete() элемента управления источником данных, то выполняется следующая последовательность шагов:
- Выполняется метод Deleting источника данных
- Происходит действие удаления
- Выполняется метод Deleting источника данных
Давайте рассмотрим способ реализации отмены удаления из события Deleting элемента SqlDataSource. Основываясь на предыдущем примере, который использовал GridView, мы обновим логику страницу таким образом, что товары, у которых UnitPrice больше чем $50.00, не могут быть удалены. Для выполнения данной задачи нам необходимо добавить обработчик для события Deleting и определить цену записи, которую мы собираемся удалить. Все, что мы имеем в данном обработчике события, так это значения параметров - в данном случае, только значение ProductID. Следовательно, нам необходимо заново осуществить запрос к базе данных для того, чтобы определить значение UnitPrice товара. Как это продемонстрировано в следующем коде, для того, чтобы просто отменить удаление, необходимо установить свойство e.Cancel в True.
Protected Sub ProductsDataSource_Deleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.SqlDataSourceCommandEventArgs) Handles ProductsDataSource.Deleting
'Определяем, имеет ли удаляемый товар цену > $50
Dim productID As Integer = Convert.ToInt32(e.Command.Parameters("@ProductID").Value)
'Определяем цену единицы для товара, который пользователь хочет удалить ...
'Существует несколько способов реализации - использовать элемент SqlDataSource, написать код запроса к базе данных ...
'Давайте используем код доступа к базе данных
Dim myConnection As New SqlConnection(ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString)
Const strSql As String = "SELECT UnitPrice FROM Products WHERE ProductID = @ProductID"
Dim myCommand As New SqlCommand(strSql, myConnection)
myCommand.Parameters.AddWithValue("@ProductID", productID)
myConnection.Open()
Dim price As Object = myCommand.ExecuteScalar
myConnection.Close()
'Запретить удаление в случае, если цена не равна NULL и она больше $50
If Not Convert.IsDBNull(price) AndAlso Convert.ToDecimal(price) > 50 Then
e.Cancel = True 'Cancel the delete
CannotDeleteMessage.Visible = True 'Отображаем сообщение с объяснением
End If
End Sub
Заметьте, что данные примеры используют код ADO.NET для доступа к информации базы данных и выполнению запроса для определения UnitPrice товара, который мы собираемся удалить. Значение ProductID данного товара возвращается из набора Parameters объекта e.Command. (Данный код возвращения параметра специфичен для типа используемого элемента источника данных; другими словами, код, рассмотренный здесь, был бы другим при использовании элемента управления ObjectDataSource.) Если значение UnitPrice не является NULL и превышает $50.00, то в данном случае удаление отменяется и элемент управления Label (CannotDeleteMessage) отображает информацию о том, что пользователь не может удалить указанный товар из-за цены.
Следующее изображение демонстрирует то, что произойдет при попытке удалить "Carnarvon Tigers", товар, который стоит больше чем $50.00.

Модификация интерфейса редактирования
Элемент управления GridView может работать в тандеме вместе с элементом SqlDataSource для того, чтобы предоставить простой интерфейс редактирования, составленный из текстовых полей. Данный упрощенный интерфейс позволяет разработчикам создавать интерфейсы редактирования без необходимости написания и строки кода, интерфейс настолько прост, что он используется (применим только к простым задачам) только для простых задач. Некоторые ограничения можно привести на примере обновления категории товара. Вкратце, вам придется (пришлось бы) обновить CategoryID значения, то есть для того, чтобы поменять категорию товара с напитков на молочные, вам придется поменять значение CategoryID с одного значения на другое. В таких случаях упрощенный интерфейс имеет такой недостаток, как отсутствие проверки вводимых значений, что может оказаться необходимым при редактировании требуемых полей, либо для полей, которые должны впоследствии быть отображены в определенном формате (к примеру, даты, числа).
К счастью, интерфейс редактирования GridView может быть настроен таким образом, что он будет включать в себя элементы управления для валидации и альтернативные элементы управления интерфейсом пользователя. Интерфейс требует немного усилий, но в большинстве случаев данные изменения могут быть выполнены посредством дизайнера (Designer) либо декларативной разметки страницы. В данной статье мы расширим упрощенный интерфейс редактирования таким образом, что он будет включать в себя элементы управления валидации для полей ProductName и UnitPrice ; мы также заменим текстовое поле CategoryID выпадающим списком, в котором перечислены все возможные категории. Читайте далее, чтобы узнать больше об этом!
Как генерируется интерфейс редактирования?
GridView может быть сделан редактируемым в случае, если он привязан к элементу управления источником данных, у которого активированы возможности обновления. Элемент управления SqlDataSource настроен так же, как и в случае если бы было указано его свойство UpdateCommand. В случае с SqlDataSource, UpdateCommand указывает выражение UPDATE либо хранимую процедуру, которая выполняется когда вызывается метод Update элемента управления источником данных.
В Visual Studio Designer GridView может быть помечен как редактируемый путем выбора опции активизации редактирования ("Enable Editing") в смарт-теге GridView либо вручную добавлением CommandField к GridView, чье свойство ShowEditButton установлено в True. Со стороны пользователя это будет выглядеть как колонка кнопок редактирования, добавленной к сетке. Нажатие на кнопку вызовет запуск постбэка и выполнение события RowEditing GridView. После того, как событие выполняется, GridView обрабатывает себя, но выбранная строка будет в режиме редактирования.
Все колонки редактируемой строки также переходят в режим редактирования. Для BoundFields, чье свойство ReadOnly не равно True используется элемент управления TextBox и его свойству Text присваивается значение данной клетки. CheckBoxFields обрабатываются вместе с активизируемой кнопкой с зависимой фиксацией. В случае когда используется BoundFields и CheckBoxField режим редактирования будет составлен из элементов управления TextBox и активированным элементом управления CheckBox.
Для того чтобы модифицировать интерфейс редактирования – чтобы он проверял вводимые значения или чтобы использовал альтернативные элементы управления, - нам необходимо использовать TemplateField вместо BoundField. TemplateFields составлены из редактируемых и только читаемых шаблонов (read-only). Шаблон позволяет разработчику указать набор статического HTML, элементов управления и синтаксиса привязки данных в пределах шаблона. При отображении строки в режиме редактирования для любых TemplateFields их EditItemTemplates используются для обработки интерфейса редактирования. Если TemplateField не хватает EditItemTemplate , то вместо него используется ItemTemplate.
Шаги модификации интерфейса редактирования
Для того чтобы модифицировать интерфейс редактирования для конкретной колонки в GridView, нам необходимо использовать TemplateField. Данная модификация подразумевает следующие шаги:
- Добавьте новый TemplateField к колонке GridView для интерфейса редактирования. В качестве альтернативы, если у вас уже есть BoundField вы можете преобразовать существующий BoundField в TemplateField нажав ссылку "Convert this field into a TemplateField" ("Преобразовать данное поле в TemplateField" ) в диалоговом окне полей (Fields). Чтобы открыть данное окно нажмите на ссылку "Edit Columns" ("Редактировать колонки") из смарт-тега GridView. Далее выберите колонку, которую вы хотите преобразовать в TemplateField и нажмите на ссылку.
- Отредактируйте EditItemTemplate. Это можно сделать, используя декларативную разметку страницы путем ввода вручную синтаксиса HTML и элемента управления. Если вам по душе дизайн, то нажмите на ссылку Edit Templates (редактирование шаблонов) в смарт-теге GridView. Отсюда вы можете выбрать шаблон, который отредактируете, и затем перетащить элементы управления из инструментария (Toolbox) в интерфейс дизайна шаблона.
- Если вы создаете альтернативный пользовательский интерфейс для редактирования, то вы должны добавить необходимые элементы управления к EditItemTemplate.
- Добавьте любые элементы управления валидацией и настройте их свойство по мере необходимости.
- Если вы используете альтернативный пользовательский интерфейс или же добавляете TemplateField вручную, то вам необходимо использовать двустороннюю привязку данных для того, чтобы ассоциировать значение параметра обновления со свойством элемента управления в EditItemTemplate. Данный шаг не является обязательным в случае, если вы преобразовали существующий BoundField в TemplateField, при этом не изменяли или добавляли новые элементы управления для сбора пользовательского ввода, потому что процесс преобразования автоматически добавляет TextBox к EditItemTemplate и использует двустороннюю привязку данных для того, чтобы ассоциировать поле данных со свойством Text элемента TextBox.
Двусторонняя привязка данных является специализированным синтаксисом, который оповещает GridView о том, что он должен взять конкретное значение из интерфейса редактирования и связать его с конкретным параметром в наборе UpdateParameters его элемента управления источником данных. К примеру, в GridView мы имеем поле с данными, названое ProductName , которое отображает название товара. Существует параметр в наборе UpdateParameters, также названный ProductName, и данный параметр используется в UpdateCommand элемента управления источником. Если мы используем TemplateField вместо BoundField для колонки ProductName , то нам понадобится использовать двустороннюю привязку, которая говорит GridView: "Эй, когда пользователь нажимает кнопку Update (обновление), тебе необходимо присвоить значение данного свойства Text элемента TextBox параметру ProductName.
В дополнение к присвоению значения, введенного пользователем в соответствующий параметр обновления, двусторонняя привязка данных также устанавливает значение свойства Text элемента TextBox в значение поля данных в момент, когда нажимается кнопка Edit данной записи. Вкратце, нам не нужно писать никакого кода, благодаря двусторонней привязке. Редактирование при помощи элемента DataGrid в ASP.NET 1.x требует написания кода по заполнению интерфейса редактирования значениями тогда, когда редактируется строка, а также получение обновленных пользователем значений и сохранение их в базе данных.
Применяем двустороннюю привязку к данным
Существует два способа указать двустороннюю привязку к данным: вручную, указав ее посредством декларативного синтаксиса страницы, либо используя диалоговое окно Edit DataBindings (Редактирование привязки данных). Давайте исследуем оба подхода. Для того чтобы ввести ее, используя дизайнер (Designer), откройте интерфейс редактирования шаблонов (Edit Templates) и выберите соответствующий шаблон. Для получения информации должны быть элементы сбора пользовательского ввода. Это может быть TextBox, DropDownList либо CheckBox. Мы хотим использовать двустороннюю привязку к данному элементу управления (и не на элементах управления валидацией либо элементах в шаблоне, которые не собирают пользовательский ввод). Из смарт-тега данного элемента управления нажмите ссылку "Edit DataBindings" (Редактировать привязки данных). Это откроет следующее диалоговое окно.

Свойства привязки элемента управления перечислены слева. Выберите одно, и затем выберите поле справа, к которому вы хотите привязать. В качестве альтернативы вы можете ввести привязку вручную в текстовом поле Custom binding. Используйте следующий синтаксис: Bind("DataField_Name").
Диалоговое окно "Edit DataBindings" просто внедряет соответствующую разметку двусторонней привязки в декларативный синтаксис элемента управления. Вы можете ввести ее вручную вместо того, чтобы использовать диалоговое окно Edit DataBindings. К примеру, для того чтобы привязать свойство Text текстового элемента ProductName к полю ProductName используйте следующий декларативный синтаксис в EditItemTemplate элемента TemplateField:
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server" Text='<%# Bind("ProductName") %>'></asp:TextBox>
</EditItemTemplate>
При вводе данного синтаксиса вручную важно не забыть отделить значение свойства при помощи апострофов вместо кавычек, поскольку кавычки используются в пределах выражения Bind. То есть, я использую Text='<%# Bind("ProductName") %>' вместо Text="<%# Bind("ProductName") %>".
Добавляем элементы управления валидацией к интерфейсу редактирования
Пример с GridView мог бы включать в себя четыре колонки: ProductName, UnitPrice, CategoryID и Discontinued. Первые три колонки отображаются в BoundFields, что в результате будет элементом TextBox для интерфейсов редактирования. Данные интерфейсы редактирования страдают нехваткой валидации вводимой информации. В случае, если пользователь введет неправильную информацию и попытается обновить базу данных, будет создано исключение. К примеру, ProductName не допускает значения NULL, так что в случае, если пользователь опустит в данном месте значение, то будет сгенерирована исключительная ситуация. Аналогично UnitPrice является полем типа money. Если пользователь попытается ввести какое-нибудь неправильное значение (к примеру, "Дешево!!"), то база данных создаст исключительную ситуацию, поскольку нет возможности преобразовать текст в денежное значение.
Пример, приведенный в конце статьи, использует TemplateFields для колонок ProductName и UnitPrice, поэтому есть возможность добавить элементы управления валидацией. RequiredFieldValidator добавляется к колонке ProductName, в то время как UnitPrice использует CompareValidator для того, чтобы обеспечить правильность вводимого значения.

Элементы управления валидацией ASP.NET 2.0 включают в себя свойство ValidationGroup которое может разделять элементы на странице на различные группы. Данное свойство полезно для страниц, на которых расположено множество мест где данные могут быть отредактированы. В любом случае, если вам необходимо использовать свойство ValidationGroup элементов управления валидацией в специализированных интерфейсах редактирования колонок GridView, то важно настроить свойство ValidationGroup CommandField в то же самое значение.
Используем выпадающий список (Drop-Down List) вместо текстового поля (TextBox)
Для определенных текстовых полей наличие TextBox не будет иметь никакого смысла. Поле CategoryID как раз является примером этого. В таблице Products базы данных каждому товару назначается категория при помощи поля CategoryID. Таблица Categories содержит запись для каждой существующей категории. При редактировании данных при помощи GridView, используя стандартный интерфейс редактирования TextBox, пользователь должен отредактировать категорию, вписав соответствующее значение CategoryID. Такой интерфейс не является наиболее удобным, так как он предполагает, что пользователь знает значение ID категории. Более того, если пользователь введет неправильное значение ID (то есть такое ID , которое не совпадет со значением записи в таблице Categories) и нажмет кнопку обновления (Update), то будет создана исключительная ситуация из-за ограничения по внешнему ключу между таблицами Products и Categories.
Уже ясно то, что нам нужен интерфейс получше. Для такого просмотра данных наиболее приемлемым будет использование выпадающего списка. Мы хотим, чтобы наш DropDownList перечислял все возможные категории и автоматически выбирал категорию редактируемого товара. После этого пользователь может изменить категорию товара, выбрав другую из списка.
Для начала создайте TemplateField либо преобразуйте существующий CategoryID, принадлежащий BoundField, в TemplateField. Далее отредактируйте EditItemTemplate таким образом, чтобы оно содержало только элемент управления DropDownList. Из смарт-тега DropDownList привяжите его к новому элементу SqlDataSource , который возвращает колонки CategoryID и CategoryName из таблицы Categories, отсортированной по CategoryName. Если вы посетите страницу в этот момент и отредактируете запись, то вы увидите выпадающий список, заполненный категориями; тем не менее, конкретная категория товара не будет выбрана, и, более того, когда вы сохраните товар, она исчезнет из списка!
Данное поведение существует потому, что нам еще предстоит применить двустороннюю привязку данных к DropDownList. Без данной информации GridView не выберет соответствующий элемент в списке при нажатии кнопки редактирования (Edit). Когда запись обновлена, отсутствуие синтаксиса двусторонней привязки в результате приравняет значение CategoryID к NULL. Поскольку SelectCommand элемента SqlDataSource использует INNER JOIN для Products и Categories, любые товары без соответствующей категории не будут возвращены запросом и тем самым не будут отображены в табличной сетке.
Для того, чтобы решить эту проблему, мы используем двустороннюю привязку данных для привязки CategoryID к свойству SelectedValue элемента DropDownList. Следующий декларативный синтаксис демонстрирует EditItemTemplate для колонки CategoryID. Заметьте что существуют DropDownList и элемент SqlDataSource, который возвращает набор всех категорий. Также исследуйте двустороннюю привязку данных, используемую в разметке элемента DropDownList.
<EditItemTemplate>
<asp:DropDownList ID="CategoriesDDL" runat="server" DataSourceID="CategoriesDataSource"
DataTextField="CategoryName" DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
</asp:DropDownList>
<asp:SqlDataSource ID="CategoriesDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT [CategoryID], [CategoryName] FROM [Categories] ORDER BY [CategoryName]">
</asp:SqlDataSource>
</EditItemTemplate>

Обработка пустых полей базы данных (NULL)
Мы уже рассматривали способ настройки элемента SqlDataSource таким образом, чтобы он выполнял выражения UPDATE к базе данных, а также способ настройки элемента GridView для работы в паре с элементом SqlDataSource, чтобы предоставить интерфейс редактирования. По умолчанию, интерфейс редактирования GridView обрабатывает TextBox для каждой редактируемой колонки в табличной сетке. Тем не менее, в определенных случаях нам может понадобиться специализация интерфейса редактирования, в частности, включение элементов управления валидацией или использование альтернативных элементов управления сбора пользовательского ввода. Мы специализировали интерфейс редактирования таким образом, что при редактировании товара из базы данных Northwind пользователь мог бы выбрать категорию товара при помощи элемента управления DropDownList вместо того, чтобы вводить значение CategoryID в поле TextBox.
В случае специализации интерфейса редактирования появляется один недостаток - данный подход может не работать с товарами, которые не имеют значения CategoryID (NULL) в базе данных. В том случае, если запрос SELECT, применяемый для заполнения GridView, использовал INNER JOIN, значит, этот запрос возвращал только те товары, которые принадлежали какой-либо категории. Даже если мы обновим запрос так, чтобы он использовал LEFT JOIN, DropDownList в интерфейсе редактирования не будет включать в себя элементы, представляющие пустое значение (NULL), так что пользователь не сможет редактировать товар и изменить его категорию с существующего значения на NULL.
В данной статье мы рассмотрим способ специализации DropDownList в интерфейсе редактирования таким образом, чтобы он правильно обрабатывал пустые значения базы данных. Читайте далее, чтобы узнать больше об этом!
Выборка товаров, у которых значение CategoryID равно NULL
Элемент SqlDataSource, используемый в статье "Модификация интерфейса редактирования", возвращает данные о товарах из таблицы Products, включая соответствующее название категории из таблицы Categories. Это было выполнено посредством следующего выражения SelectCommand:
SELECT [ProductID], [ProductName], [Products].[CategoryID],
[CategoryName], [UnitPrice], [Discontinued]
FROM [Products]
INNER JOIN [Categories] ON
[Products].[CategoryID] = [Categories].[CategoryID]
ORDER BY [ProductName]
INNER JOIN возвращает совпавшие строки Categories , что позволяет нам получать CategoryName для товаров связанных со значением CategoryID.
Колонка CategoryID таблицы Products может содержать пустые значения (NULL), но INNER JOINs пропускают строки, где имеются пустые поля по объединенной колонке. Вкратце, INNER JOIN , используемое в предыдущем запросе, возвращает только те товары, которые имеют не пустое значение CategoryID . Для того, чтобы вернуть все товары, независимо от того имеют ли они значение NULL в CategoryID, нам нужно использовать LEFT JOIN.
SELECT [ProductID], [ProductName], [Products].[CategoryID],
[CategoryName], [UnitPrice], [Discontinued]
FROM [Products]
LEFT JOIN [Categories] ON
[Products].[CategoryID] = [Categories].[CategoryID]
ORDER BY [ProductName]
Изменив синтаксис объединения JOIN на LEFT JOIN, мы получим табличную сетку, включающую в себя и те товары, у которых значение CategoryID равно NULL. По умолчанию, значения NULL отображаются в виде пустой строки в ItemTemplate. К примеру, следующее изображение показывает товар Boston Crab Meat, у которого значение CategoryID равно NULL, и, как это заметно, отображено оно в виде пустой строки в колонке Category Name.

Изменение к левому объединению (Left Join) является всего лишь первым шагом. Нам также необходимо обновить CategoriesDDL DropDownList в интерфейсе редактирования колонки Category Name. На данный момент CategoriesDDL привязан к набору категорий в таблице Categories. То есть, нет ни одной записи, которая соответствовала бы значению NULL в базе данных. Следовательно, если мы попытаемся отредактировать товар, у которого значение CategoryID равно NULL, то мы получим ошибку. Ошибка произойдет потому, что в EditItemTemplate мы привязываем значение CategoryID редактируемого товара к свойству SelectedValue элемента CategoriesDDL DropDownList, в то время как CategoriesDDL DropDownList не имеет такого значения.

Модифицируем элемент управления CategoriesDDL DropDownList так, чтобы он мог обрабатывать значения NULL
Нам необходимо обновить список элементов в CategoriesDDL DropDownList таким образом, чтобы он включал в себя полный набор возможных категорий, а также элемент, соответствующий значению NULL базы данных. Объекты Parameter в SqlDataSource (либо ObjectDataSource), сами преобразят значения NULL в пустые строки и обратно. Тем самым все, что нам надо сделать для того, чтобы все это получилось - добавить в список CategoriesDDL DropDownList элемент, который имеет значение пустой строки.
Как только мы внесем пустую строку как элемент списка в CategoriesDDL DropDownList, двусторонняя привязка данных будет работать идеально. Когда строку, у которой CategoryID равно NULL, будет редактироваться, значение NULL будет преобразовано в пустую строку и соответствующий элемент выпадающего списка будет выбран. Аналогично, если в списке будет выбрана пустая строка и затем она будет передана в базу данных, то в результате будет сохранено значение NULL.
Для того чтобы добавить элемент в список DropDownList со значением пустой строки нам необходимо сделать две вещи :
- Установить свойство AppendDataBoundItems элемента DropDownList в True.
- Добавить <asp:ListItem> к декларативной разметке элемента, установить Value="".
После того, как мы применим все эти действия, разметка Category Name TemplateField будет выглядеть следующим образом:
<asp:TemplateField HeaderText="CategoryName" SortExpression="CategoryName">
<EditItemTemplate>
<asp:DropDownList ID="CategoriesDDL" runat="server" DataSourceID="CategoriesDataSource" AppendDataBoundItems="True"
DataTextField="CategoryName" DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
<asp:ListItem Text="(None)" Value=""></asp:ListItem>
</asp:DropDownList>
<asp:SqlDataSource ID="CategoriesDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT [CategoryID], [CategoryName] FROM [Categories] ORDER BY [CategoryName]">
</asp:SqlDataSource>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("CategoryName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Разметка, добавленная согласно двум указанным шагам, отмечена красным цветом. Обратите внимание на установление свойства AppendDataBoundItems. Если свойство AppendDataBoundItems установлено в False (по умолчанию), то категории, привязанные к DropDownList при помощи SqlDataSource в EditItemTemplate перепишут элемент списка для значения NULL базы данных.
Второй шаг вручную добавляет элемент к списку DropDownList. Свойству Text элемента списка можно присвоить любое значение (здесь мы используем "(None)"), но важно помнить, что свойство Value может быть явно установлено в пустую строку. То есть, обязательным условием является то, что разметка ListItem включает в себя Value="". Если вы опустите данное условие, то свойство Value возвратит то же самое значение, что хранится в свойстве Text. Также предупреждаю вас о том, что свойство Value не может быть приравнено к пустой строке посредством дизайнера (Designer). Попытка просто напросто пропустит свойство Value в декларативном синтаксисе. Вы должны ввести данную разметку через декларативный синтаксис.
На данный момент GridView позволяет отображать товары со значением NULL в CategoryID и позволяет редактировать такие товары, а также назначать значение NULL категории товара.

Вывод
Элементы управления источником данных в ASP.NET 2.0 обычно используются декларативно, предоставляя возможность работы с данными без необходимости написания кода. В случаях, когда необходимо получить информацию, вставить, обновить либо удалить ее программным путем, вы можете сэкономить некоторый объем кода при помощи элементов управления источником данных, как мы уже убедились в данной статье. Просто добавьте элемент управления SqlDataSource или AccessDataSource на страницу, настройте его свойства, и затем вызовите указанную SQL-команду посредством вызова метода Select(), Insert(), Update()или Delete() элемента. Данная статья демонстрирует два примера: в одном из них информация была получена, а во втором - вставлена. Приложение к данной статье предоставляет полный код данных примеров, а также дополнительный пример по использованию элемента SqlDataSource для программного удаления информации из базы данных.
Scott Mitchell
Скачать исходники примеров: часть 1, часть 2, часть 3, часть 4, часть 5.