Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1678 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 13:

Привязка данных 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 следующие компоненты в порядке их перечисления:
    • ObjectDataSource из вкладки Data
    • Дескриптор <h2>Выберите ID сотрудника</h2>
    • ListBox из вкладки Standard
    • ObjectDataSource из вкладки Data
    • Дескриптор <h2>Данные о сотруднике</h2>
    • DetailsView из вкладки Data

Настроим компоненты страницы ObjectDataSourceParameters.aspx декларативным способом в режиме Design через панель Properties.

  • Выделите первый источник ObjectDataSource1 и установите для него следующие свойства
    • SelectMethod="GetAllEmployees" - автоматически вызываемый метод
    • TypeName="EmployeeDB" - применяемый пользовательский класс
  • Выделите список ListBox1 и установите для него следующие свойства
    • DataSourceID="ObjectDataSource1" - подключенный источник данных
    • DataTextField="EmployeeID" - видимые данные списка
    • AutoPostBack="True" - инициирует обратную отсылку списка
    • Rows=9 - количество видимых строк
    • Width=100 - ширина в пикселах
  • Выделите второй источник 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 и установите для него следующие свойства
    • DataSourceID="ObjectDataSource2"
    • AutoGenerateRows="False" - подавляет самостоятельное генерирование строк для всех полей без разбора
  • Запустите страницу 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 сотрудника


Данные о сотруднике

LastName Снетков
TitleOfCourtesy Доцент 007
EmployeeID 9
FirstName Владимир

Аналогичным образом можно задействовать и другие методы нашего пользовательского класса 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 подкорректируйте так:
    • DataSourceID="ObjectDataSource1" - поменять подключаемый источник данных
    • <asp:BoundField DataField="City" HeaderText="Проживает" /> - удалить строку с отсутствующим в SQL-команде полем
    • <asp:BoundField DataField="TitleOfCourtesy" HeaderText="Статус" /> - изменить имя поля на имеющееся в SQL-команде

В окончательном виде страница 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-результат
№ п/п Имя Фамилия Статус Изменить
1 Nancy Davolio Ms.
2 Andrew Fuller Dr.
3 Janet Leverling Ms.
4 Margaret Peacock Mrs.
5 Steven Buchanan Mr.
6 Michael Suyama Mr.
7 Robert King Mr.
8 Laura Callahan Ms.
9 Владимир Снетков Доцент 007