Привязка данных 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-результат