Привязка данных ADO.NET
Использование ObjectDataSource с параметризованным методом пользовательского класса
По синтаксису настройки компонента ObjectDataSource мы не можем использовать ни параметризованный конструктор, ни параметризованный метод, поскольку свойствам SelectMethod, InsertMethod, DeleteMethod и UpdateMethod можно присваивать только имя метода. Реальные же методы пользовательского класса могут содержать параметры. Например, в нашем классе EmployeeDB содержатся следующие параметризованные методы:
- public int InsertEmployee(EmployeeDetails emp) {...}
- public EmployeeDetails GetEmployee(int employeeID) {...}
- public void DeleteEmployee(int employeeID) {...}
- public void UpdateEmployee(int employeeID, string firstName,string lastName, string titleOfCourtesy) {...}
Мы воспользуемся методом GetEmployee(int employeeID), который ранее был описан так
public EmployeeDetails GetEmployee(int employeeID)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("GetEmployee", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4));
cmd.Parameters["@EmployeeID"].Value = employeeID;
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
// Получить первую строку
reader.Read();
EmployeeDetails emp = new EmployeeDetails(
(int)reader["EmployeeID"],
(string)reader["FirstName"],
(string)reader["LastName"],
(string)reader["TitleOfCourtesy"]);
reader.Close();
return emp;
}
catch
{
throw new ApplicationException("Ошибка данныx.");
}
finally
{
con.Close();
}
}Метод GetEmployee() вызывает хранимую процедуру GetEmployee с параметром, имеющую следующий вид
// Выбрать запись
string sql6 =
"CREATE PROCEDURE GetEmployee "
+ "@EmployeeID int "
+ "AS "
+ "SELECT EmployeeID, FirstName, LastName, TitleOfCourtesy "
+ "FROM Employees "
+ "WHERE EmployeeID = @EmployeeID";Сейчас наша задача - создать страницу на основе компонента ObjectDataSource и пользовательского класса EmployeeDB, в которой при загрузке автоматически заполнялся бы список значениями EmployeeID из таблицы Employees, а при щелчке пользователя на конкретном элементе списка выдавались бы остальные сведения о выбранном сотруднике.
-
Создайте новую страницу с совмещенным кодом под именем ObjectDataSourceParameters.aspx и назначьте ее стартовой -
Поместите на страницу из панели Toolbox следующие компоненты в порядке их перечисления:
Настроим компоненты страницы ObjectDataSourceParameters.aspx декларативным способом в режиме Design через панель Properties.
-
Выделите первый источник ObjectDataSource1 и установите для него следующие свойства- SelectMethod="GetAllEmployees" - автоматически вызываемый метод
- TypeName="EmployeeDB" - применяемый пользовательский класс
-
Выделите список ListBox1 и установите для него следующие свойства
-
Выделите второй источник ObjectDataSource2 и установите для него следующие свойства- SelectMethod="GetEmployee" - автоматически вызываемый метод
- TypeName="EmployeeDB" - применяемый пользовательский класс
-
Щелчком на кнопке ( ...) в поле свойства ObjectDataSource2.SelectParameters вызовите диалоговое окно, которое заполните так
Работа этого окна трансформируется в следующий дескрипторный код разметочной части страницы
<asp:ObjectDataSource ID="ObjectDataSource2" runat="server"
SelectMethod="GetEmployee" TypeName="EmployeeDB"
OnSelecting="ObjectDataSource2_Selecting" >
<SelectParameters>
<asp:ControlParameter ControlID="ListBox1"
Name="employeeID" PropertyName="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>Имя для передаваемого в источник ObjectDataSource2 параметра должно повторять имя формального параметра, указанного при объявлении метода
public EmployeeDetails GetEmployee(int employeeID) {...}поскольку источник допускает перегрузку прикрепленного метода на основании анализа числа переданных параметров и их имен в объявлении метода (явного-то вызова метода нет!).
Поэтому порядок следования ожидаемых параметров в настройках самого ObjectDataSource, их типы и регистр не учитываются, главное - точные имена и их количество. В каком регистре набирается имя параметра при декларативном объявлении в разметочной части страницы - значения не имеет, лишь-бы оно повторяло имя формального аргумента в объявлении подключаемого метода.
-
Выделите элемент отображения данных DetailsView1 и установите для него следующие свойства
-
Запустите страницу ObjectDataSourceParameters.aspx и получите ошибку выполнения!!!
Дело здесь в том, что когда страница запрашивается в первый раз, нет никакого выбранного значения в элементе ListBox1, но элемент ObjectDataSource2 все равно вызовет метод GetEmployee() с неопределенным значением employeeID. Это и вызовет генерацию исключения в поиске данных. Ситуацию можно исправить, если перехватить попытку привязки в элементе ObjectDataSource2 и явно отменить ее, когда параметр employeeID не определен.
Для этого нужно обработать событие ObjectDataSource.Selecting, которое как раз происходит перед выполнением SQL-запроса по поиску данных в базе данных. Если мы проверим коллекцию InputParameters и не обнаружим в ней параметра employeeID, то работу ObjectDataSource2 нужно будет прервать.
-
Создайте в панели Properties для события ObjectDataSource2.Selecting обработчик, который заполните так, чтобы окончательный код страницы ObjectDataSourceParameters.aspx был следующим
<%@ Page Language="C#" %>
<script runat="server">
protected void ObjectDataSource2_Selecting(object sender,
ObjectDataSourceSelectingEventArgs e)
{
if (e.InputParameters["employeeID"] == null)
e.Cancel = true;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
SelectMethod="GetAllEmployees" TypeName="EmployeeDB" />
<h2>
Выберите ID сотрудника</h2>
<asp:ListBox ID="ListBox1" runat="server" AutoPostBack="True"
DataSourceID="ObjectDataSource1" DataTextField="EmployeeID"
Rows="9" Width="100px" />
<asp:ObjectDataSource ID="ObjectDataSource2" runat="server"
SelectMethod="GetEmployee" TypeName="EmployeeDB"
OnSelecting="ObjectDataSource2_Selecting" >
<SelectParameters>
<asp:ControlParameter ControlID="ListBox1"
Name="employeeID" PropertyName="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>
<h2>
Данные о сотруднике</h2>
<asp:DetailsView ID="DetailsView1" runat="server"
Height="50px" Width="125px" DataSourceID="ObjectDataSource2"
AutoGenerateRows="False" >
<Fields>
<asp:BoundField DataField="LastName"
HeaderText="LastName" SortExpression="LastName" />
<asp:BoundField DataField="TitleOfCourtesy"
HeaderText="TitleOfCourtesy" SortExpression="TitleOfCourtesy" />
<asp:BoundField DataField="EmployeeID"
HeaderText="EmployeeID" SortExpression="EmployeeID" />
<asp:BoundField DataField="FirstName"
HeaderText="FirstName" SortExpression="FirstName" />
</Fields>
</asp:DetailsView>
</div>
</form>
</body>
</html>Элемент управления DetailsView отображает только одну запись в виде таблицы с двумя столбцами, один из которых содержит имена поле, второй - их значения.
-
Исполните страницу ObjectDataSourceParameters.aspx и получите результат
Выберите ID сотрудника
Данные о сотруднике
Аналогичным образом можно задействовать и другие методы нашего пользовательского класса EmployeeDB, имеющие параметры:
- public int InsertEmployee(EmployeeDetails emp) {...}
- public void DeleteEmployee(int employeeID) {...}
- public void UpdateEmployee(int employeeID, string firstName, string lastName, string titleOfCourtesy) {...}
Использование ObjectDataSource для обновления записей
Продемонстрируем возможность подключения к ObjectDataSource метода UpdateEmployee() пользовательского класса EmployeeDB, предназначенного для обновления записей таблицы Employees. За одно посмотрим, как правильно вызвать перегрузку этого метода, если в качестве параметра используется другой пользовательский тип, экземпляр которого прежде нужно создать, и только потом передать компоненту ObjectDataSource как параметр.
Применение исходного метода UpdateEmployee()
Вначале используем метод UpdateEmployee() в том виде с четырьма параметрами, как он был разработан нами ранее. Для сокращения работы используем часть наработок страницы SqlDataSourceUpdates.aspx. Метод UpdateEmployee() реализует SQL-команду хранимой процедуры UpdateEmployee, имеющей следующий вид
// Обновление записи
string sql3 =
"CREATE PROCEDURE UpdateEmployee "
+ "@EmployeeID int, "
+ "@FirstName varchar(10),"
+ "@LastName varchar(20),"
+ "@TitleOfCourtesy varchar(25) "
+ "AS "
+ "UPDATE Employees "
+ "SET "
+ "FirstName = @FirstName,"
+ "LastName = @LastName,"
+ "TitleOfCourtesy = @TitleOfCourtesy "
+ "WHERE EmployeeID = @EmployeeID";Сам существующий метод UpdateEmployee() описывается так в файле App_Code/UpdateEmployeeDB.cs
public void UpdateEmployee(int employeeID, string firstName,
string lastName, string titleOfCourtesy)
{
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("UpdateEmployee", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4));
cmd.Parameters["@EmployeeID"].Value = employeeID;
cmd.Parameters.Add(new SqlParameter("@FirstName", SqlDbType.NVarChar, 10));
cmd.Parameters["@FirstName"].Value = firstName;
cmd.Parameters.Add(new SqlParameter("@LastName", SqlDbType.NVarChar, 20));
cmd.Parameters["@LastName"].Value = lastName;
cmd.Parameters.Add(new SqlParameter("@TitleOfCourtesy", SqlDbType.NVarChar, 25));
cmd.Parameters["@TitleOfCourtesy"].Value = titleOfCourtesy;
try
{
con.Open();
cmd.ExecuteNonQuery();
}
catch
{
throw new ApplicationException("Ошибка данныx.");
}
finally
{
con.Close();
}
}-
Скопируйте страницу SqlDataSourceUpdates.aspx с именем ObjectDataSourceUpdates1.aspx и назначьте ее стартовой -
Запустите страницу ObjectDataSourceUpdates1.aspx на выполнение и удостовертесь, что она работоспособна -
Удалите элемент SqlDataSource1 и на его месте расположите объект ObjectDataSource1
-
Настройте объект ObjectDataSource1 следующим образом- TypeName="EmployeeDB"
- SelectMethod="GetAllEmployees"
- UpdateMethod="UpdateEmployee"
-
Объект GridView1 подкорректируйте так:
В окончательном виде страница ObjectDataSourceUpdates1.aspx должна стать такой
<%@ Page Language="C#" %>
<script runat="server">
// Здесь мы не написали ни строчки кода!!!
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
TypeName="EmployeeDB"
SelectMethod="GetAllEmployees"
UpdateMethod="UpdateEmployee" />
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
DataSourceID="ObjectDataSource1">
<Columns>
<asp:BoundField DataField="EmployeeID" HeaderText="№ п/п" />
<asp:BoundField DataField="FirstName" HeaderText="Имя" />
<asp:BoundField DataField="LastName" HeaderText="Фамилия" />
<asp:BoundField DataField="TitleOfCourtesy" HeaderText="Статус" />
<asp:CommandField ButtonType="Button" HeaderText="Изменить"
ShowEditButton="True" ShowHeader="True"
EditText="Редакция" CancelText="Отмена" UpdateText="Применить" >
<HeaderStyle BackColor="Red" ForeColor="Yellow" />
</asp:CommandField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>Метод UpdateEmployee() автоматически получает от объекта отображения GridView1 значения параметров для обновления за счет того, что имена формальных аргументов метода совпадают с именами полей элемента отображения (регистр не учитывается). Если в определении метода изменить имя хотя-бы одного формального аргумента, то страница выдаст ошибку о запросе несуществующего в таблице Employees поля.
-
Исполните страницу ObjectDataSourceUpdates1.aspx и убедитесь, что она функционирует в соответствии с задуманным и в одном из вариантов генерирует такой HTML-результат

