Элементы управления источниками данных

От Майкрософт

Элемент управления DataGrid в ASP.NET 1.x отметил большое улучшение доступа к данным в веб-приложениях. Тем не менее, это было не так удобно, как это могло быть. Он по-прежнему требует значительного объема кода для получения из него много полезных функций. Это модель во всех попытках доступа к данным в 1.x.

Элемент управления DataGrid в ASP.NET 1.x отметил большое улучшение доступа к данным в веб-приложениях. Тем не менее, это было не так удобно, как это могло быть. Он по-прежнему требует значительного объема кода для получения из него много полезных функций. Это модель во всех попытках доступа к данным в 1.x.

ASP.NET 2.0 частично решает эту проблему с помощью элементов управления источниками данных. Элементы управления источниками данных в ASP.NET 2.0 предоставляют разработчикам декларативную модель для получения данных, отображения данных и редактирования данных. Цель элементов управления источниками данных — обеспечить согласованное представление данных для элементов управления, привязанных к данным, независимо от источника этих данных. В основе элементов управления источником данных в ASP.NET 2.0 находится абстрактный класс DataSourceControl. Класс DataSourceControl предоставляет базовую реализацию интерфейса IDataSource и интерфейса IListSource, последняя из которых позволяет назначить элемент управления источником данных в качестве источника данных элемента управления с привязкой к данным (через новое свойство DataSourceId, рассмотренное позже) и предоставить данные в виде списка. Каждый список данных из элемента управления источниками данных предоставляется в виде объекта DataSourceView. Доступ к экземплярам DataSourceView предоставляется интерфейсом IDataSource. Например, метод GetViewNames возвращает ICollection, который позволяет перечислить DataSourceViews, связанные с определенным элементом управления источниками данных, и метод GetView позволяет получить доступ к конкретному экземпляру DataSourceView по имени.

Элементы управления источниками данных не имеют пользовательского интерфейса. Они реализуются как серверные элементы управления, чтобы они могли поддерживать декларативный синтаксис и чтобы они имели доступ к состоянию страницы при необходимости. Элементы управления источниками данных не отображают разметку HTML клиенту.

Замечание

Как вы увидите позже, есть также преимущества кэширования, полученные с помощью элементов управления источниками данных.

Хранение строк подключения

Прежде чем приступить к настройке элементов управления источниками данных, мы должны охватить новую возможность в ASP.NET 2.0 относительно строк подключения. ASP.NET 2.0 содержит новый раздел в файле конфигурации, который позволяет легко хранить строки подключения, которые можно читать динамически во время выполнения. Раздел <connectionStrings> упрощает хранение строк подключения.

Приведенный ниже фрагмент добавляет новую строку подключения.

<connectionStrings> <add name="Northwind" connectionString="Data Source=localhost; Integrated Security=SSPI;Initial Catalog=Northwind;" providerName="System.Data.SqlClient" /> </connectionStrings>

Замечание

В отличие от <раздела appSettings>, <раздел connectionStrings> находится вне <раздела system.web> в файле конфигурации.

Чтобы использовать эту строку подключения, можно использовать следующий синтаксис при настройке атрибута ConnectionString серверного элемента управления.

ConnectionString="<%$ ConnectionStrings:Northwind%>"

Также <раздел connectionStrings> можно зашифровать, чтобы конфиденциальная информация не предоставлялась. Эта возможность будет описана в следующем модуле.

Кэширование источников данных

Каждый элемент DataSourceControl предоставляет четыре свойства для настройки кэширования; EnableCaching, CacheDuration, CacheExpirationPolicy и CacheKeyDependency.

Включение кэширования

EnableCaching — это логическое свойство, определяющее, включено ли кеширование для контроллера источника данных.

Свойство CacheDuration

Свойство CacheDuration задает количество секунд, в течение которых кэш остается допустимым. Установка этого свойства в 0 приводит к тому, что кэш остается допустимым до тех пор, пока он не будет явно аннулирован.

Свойство CacheExpirationPolicy

Свойство CacheExpirationPolicy может иметь значение "Абсолютный" или "Скользящий". Установка значения Absolute означает, что максимальное количество времени, в течение которых данные будут кэшироваться, — это количество секунд, указанное свойством CacheDuration. Установив режим "Скользящий", срок действия будет сбрасываться при выполнении каждой операции.

Свойство CacheKeyDependency

Если строковое значение указано для свойства CacheKeyDependency, ASP.NET настроит новую зависимость кэша на основе этой строки. Это позволяет явно аннулировать кэш, просто изменив или удалив CacheKeyDependency.

Важно. Если олицетворение включено и доступ к источнику данных и /или содержимому данных основаны на идентификаторе клиента, рекомендуется отключить кэширование, установив для параметра EnableCaching значение False. Если кэширование включено в этом сценарии и запрос делает пользователь, отличный от того, который изначально запрашивал данные, авторизация в источнике данных не проверяется. Данные просто будут передаваться из кэша.

Элемент управления SqlDataSource

Элемент управления SqlDataSource позволяет разработчику получать доступ к данным, хранящимся в любой реляционной базе данных, поддерживающей ADO.NET. Он может использовать поставщик System.Data.SqlClient для доступа к базе данных SQL Server, поставщику System.Data.OleDb, поставщику System.Data.Odbc или поставщику System.Data.OracleClient для доступа к Oracle. Поэтому SqlDataSource, безусловно, не только используется для доступа к данным в базе данных SQL Server.

Чтобы использовать SqlDataSource, необходимо просто указать значение свойства ConnectionString и указать команду SQL или хранимую процедуру. Элемент управления SqlDataSource отвечает за работу с базовой архитектурой ADO.NET. Он открывает подключение, запрашивает источник данных или выполняет хранимую процедуру, возвращает данные, а затем закрывает подключение.

Замечание

Так как класс DataSourceControl автоматически закрывает подключение, оно должно сократить количество вызовов клиентов, создаваемых утечкой подключений к базе данных.

Приведенный ниже фрагмент кода привязывает элемент управления DropDownList к элементу управления SqlDataSource с помощью строки подключения, которая хранится в файле конфигурации, как показано выше.

<asp:SqlDataSource id="SqlDataSource1" runat="server" DataSourceMode="DataReader" ConnectionString="<%$ ConnectionStrings:Northwind%>" SelectCommand="SELECT EmployeeID, LastName FROM Employees"> </asp:SqlDataSource><asp:DropDownList id="ListBox1" runat="server" DataTextField="LastName" DataValueField="EmployeeID" DataSourceID="SqlDataSource1"> </asp:DropDownList>

Как показано выше, свойство DataSourceMode sqlDataSource указывает режим для источника данных. В приведенном выше примере DataSourceMode имеет значение DataReader. В этом случае SqlDataSource вернет объект IDataReader с помощью курсора с прямым и только для чтения курсором. Указанный тип возвращаемого объекта управляется используемым поставщиком. В данном случае я использую поставщика System.Data.SqlClient, как указано в <разделе connectionStrings> файла web.config. Поэтому возвращаемый объект будет иметь тип SqlDataReader. Указав значение DataSourceMode равное DataSet, данные можно хранить в наборе данных на сервере. Этот режим позволяет добавлять такие функции, как сортировка, разбиение по страницам и т. д. Если бы я связывал данные SqlDataSource с элементом управления GridView, я бы выбрал режим DataSet. Однако в случае DropDownList режим DataReader — правильный выбор.

Замечание

При кэшировании SqlDataSource или AccessDataSource свойство DataSourceMode должно иметь значение DataSet. Исключение возникает, если вы включите кэширование с DataSourceMode, установленным в DataReader.

Свойства SqlDataSource

Ниже приведены некоторые свойства элемента управления SqlDataSource.

CancelSelectOnNullParameter

Логическое значение, указывающее, отменена ли команда select, если один из параметров имеет значение NULL. По умолчанию значение — True.

Обнаружение конфликтов

В ситуации, когда несколько пользователей могут одновременно обновлять источник данных, свойство ConflictDetection определяет поведение элемента управления SqlDataSource. Это свойство принимает одно из значений, определенных в перечислении ConflictOptions. Эти значения : CompareAllValues и OverwriteChanges. Если установлено значение OverwriteChanges, последний пользователь, который записывает данные в источник данных, перезаписывает любые предыдущие изменения. Однако если для свойства ConflictDetection задано значение CompareAllValues, параметры создаются для столбцов, возвращаемых SelectCommand, и параметры также создаются для хранения исходных значений в каждом из этих столбцов, позволяя SqlDataSource определить, изменились ли значения после выполнения SelectCommand.

DeleteCommand

Задает или получает строку SQL, используемую при удалении строк из базы данных. Это может быть SQL-запрос или имя хранимой процедуры.

DeleteCommandType

Задает или получает тип команды удаления, SQL-запрос (текст) или хранимую процедуру (StoredProcedure).

DeleteParameters

Возвращает параметры, которые используются командой DeleteCommand объекта SqlDataSourceView, связанного с элементом управления SqlDataSource.

OldValuesParameterFormatString

Это свойство используется для указания формата исходных параметров значения в случаях, когда для свойства ConflictDetection задано значение CompareAllValues. По умолчанию используется {0}, что означает, что параметры значения будут иметь то же имя, что и исходный параметр. Другими словами, если имя поля — EmployeeID, исходный параметр значения будет @EmployeeID.

SelectCommand

Задает или получает строку SQL, используемую для извлечения данных из базы данных. Это может быть SQL-запрос или имя хранимой процедуры.

SelectCommandType

Задает или получает тип команды выбора, SQL-запрос (текст) или хранимую процедуру (StoredProcedure).

Selectparameters

Возвращает параметры, используемые командой выбора (SelectCommand) объекта SqlDataSourceView, связанного с контролом SqlDataSource.

ИмяПараметраСортировки

Возвращает или задает имя параметра хранимой процедуры, используемого при сортировке данных, полученных элементом управления источником данных. Допустимо только в том случае, если параметр SelectCommandType имеет значение StoredProcedure.

SqlCacheDependency

Строка с разделителями с запятой, указывающая базы данных и таблицы, используемые в зависимости кэша SQL Server. (Зависимости кэша SQL будут обсуждаться в следующем модуле.)

Команда обновления

Задает или получает строку SQL, используемую при обновлении данных в базе данных. Это может быть SQL-запрос или имя хранимой процедуры.

ТипКомандыОбновления

Задает или получает тип команды обновления, SQL-запрос (текст) или хранимую процедуру (StoredProcedure).

ОбновитьПараметры

Возвращает параметры, используемые командой обновления (UpdateCommand) объекта SqlDataSourceView, который связан с элементом управления SqlDataSource.

Элемент управления AccessDataSource

Элемент управления AccessDataSource является производным от класса SqlDataSource и используется для привязки данных к базе данных Microsoft Access. Свойство ConnectionString для элемента управления AccessDataSource является свойством только для чтения. Вместо использования свойства ConnectionString свойство DataFile используется для указания базы данных Access, как показано ниже.

<asp:AccessDataSource id="AccessDataSource1" runat="server" DataFile="~/App_Data/Northwind.mdb"> </asp:AccessDataSource>

AccessDataSource всегда задает ProviderName базового SqlDataSource на System.Data.OleDb и подключается к базе данных с помощью поставщика Microsoft.Jet.OLEDB.4.0 OLE DB. Элемент управления AccessDataSource нельзя использовать для подключения к базе данных Access, защищенной паролем. Если необходимо подключиться к защищенной паролем базе данных, следует использовать элемент управления SqlDataSource.

Замечание

Доступ к базам данных, хранящимся на веб-сайте, должен размещаться в каталоге App_Data. ASP.NET не разрешает просмотр файлов в этом каталоге. При использовании баз данных Access необходимо предоставить учетной записи процесса разрешения на чтение и запись для каталога App_Data.

Элемент управления XmlDataSource

XmlDataSource используется для привязки данных XML-данных к управляющим элементам с привязкой к данным. Можно привязать к XML-файлу с помощью свойства DataFile или привязать к XML-строке с помощью свойства Data. XmlDataSource предоставляет АТРИБУТы XML в качестве привязываемых полей. В случаях, когда необходимо привязать к значениям, которые не представлены как атрибуты, необходимо использовать преобразование XSL. Выражения XPath также можно использовать для фильтрации XML-данных.

Рассмотрим следующий XML-файл:

<?xml version="1.0" encoding="utf-8" ?> <People> <Person FirstName="Jake" LastName="Stone"> <Address> <Street>345 Maple St.</Street> <City>Redmond</City> <Region>WA</Region> <ZipCode>01434</ZipCode> </Address> <Job> <Title>CEO</Title> <Description>Develops company strategies.</Description> </Job> </Person> <Person FirstName="Jacob" LastName="Ladder"> <Address> <Street>123 Elm St.</Street> <City>Seattle</City> <Region>WA</Region> <ZipCode>11223</ZipCode> </Address> <Job> <Title>Attorney</Title> <Description>Reviews legal issues.</Description> </Job> </Person> <Person FirstName="Angela" LastName="Hound"> <Address> <Street>34 Palm Avenue</Street> <City>Renton</City> <Region>WA</Region> <ZipCode>63910</ZipCode> </Address> <Job> <Title>IT Director</Title> <Description>In charge of corporate network.</Description> </Job> </Person> </People>

Обратите внимание, что XmlDataSource использует свойство XPath people /Person для фильтрации только <на узлах Person> . Затем DropDownList привязывает данные к атрибуту LastName с помощью свойства DataTextField.

Хотя элемент управления XmlDataSource в основном используется для привязки данных к xml-данным только для чтения, можно изменить XML-файл данных. Обратите внимание, что в таких случаях автоматическое вставка, обновление и удаление информации в XML-файле не происходит автоматически, так как это делается с другими элементами управления источниками данных. Вместо этого необходимо написать код для ручного редактирования данных с помощью следующих методов элемента управления XmlDataSource.

GetXmlDocument

Извлекает объект XmlDocument, содержащий XML-код, полученный XmlDataSource.

Сохранить

Сохраняет находящийся в памяти XmlDocument обратно в исходный источник данных.

Важно понимать, что метод Save будет работать только при выполнении следующих двух условий:

  1. XmlDataSource использует свойство DataFile для привязки к XML-файлу вместо свойства Data для привязки к данным XML в памяти.
  2. Преобразование не указано с помощью свойства Transform или TransformFile.

Обратите внимание, что метод Save может дать непредвиденные результаты при вызове нескольких пользователей одновременно.

Элемент управления ObjectDataSource

Элементы управления источниками данных, которые мы рассмотрели до этой точки, являются отличным выбором для двухуровневых приложений, где управление версиями данных взаимодействует непосредственно с хранилищем данных. Однако многие реальные приложения — это многослойные приложения, где контроллер источника данных может потребовать обмена данными с бизнес-объектом, который, в свою очередь, взаимодействует с уровнем данных. В таких ситуациях ObjectDataSource отлично подходит. Объект ObjectDataSource работает вместе с исходным объектом. Элемент управления ObjectDataSource создаст экземпляр исходного объекта, вызовет указанный метод и удалит экземпляр объекта в пределах одного запроса, если объект имеет методы экземпляра вместо статических методов (Shared в Visual Basic). Поэтому объект должен быть без отслеживания состояния. То есть объект должен получить и освободить все необходимые ресурсы в пределах одного запроса. Вы можете управлять тем, как создается исходный объект, обрабатывая событие ObjectCreating элемента управления ObjectDataSource. Экземпляр исходного объекта можно создать, а затем задать для этого экземпляра свойство ObjectInstance класса ObjectDataSourceEventArgs. Элемент управления ObjectDataSource будет использовать экземпляр, созданный в событии ObjectCreating, вместо создания экземпляра самостоятельно.

Если исходный объект элемента управления ObjectDataSource предоставляет общедоступные статические методы (общие в Visual Basic), которые можно вызывать для получения и изменения данных, элемент управления ObjectDataSource будет вызывать эти методы напрямую. Если элемент управления ObjectDataSource должен создать экземпляр исходного объекта для вызова метода, объект должен включать открытый конструктор, который не принимает параметров. Элемент управления ObjectDataSource вызовет этот конструктор при создании нового экземпляра исходного объекта.

Если исходный объект не содержит открытый конструктор без параметров, можно создать экземпляр исходного объекта, который будет использоваться элементом управления ObjectDataSource в событии ObjectCreating.

Указание методов объекта

Исходный объект элемента управления ObjectDataSource может содержать любое количество методов, используемых для выбора, вставки, обновления или удаления данных. Эти методы вызываются элементом управления ObjectDataSource на основе имени метода, определяемого с помощью свойства SelectMethod, InsertMethod, UpdateMethod или DeleteMethod элемента управления ObjectDataSource. Исходный объект также может включать необязательный метод SelectCount, который определяется элементом управления ObjectDataSource с помощью свойства SelectCountMethod, который возвращает количество общих объектов в источнике данных. Элемент управления ObjectDataSource вызовет метод SelectCount после вызова метода Select, чтобы получить общее количество записей в источнике данных для использования при разбиении по страницам.

Лабораторная работа по использованию элементов управления источниками данных

Упражнение 1. Отображение данных с помощью элемента управления SqlDataSource

В следующем упражнении для подключения к базе данных Northwind используется элемент управления SqlDataSource. Предполагается, что у вас есть доступ к базе данных Northwind в экземпляре SQL Server 2000.

  1. Создание нового веб-сайта ASP.NET.

  2. Добавьте новый файл web.config.

    1. Щелкните проект правой кнопкой мыши в обозревателе решений и нажмите кнопку "Добавить новый элемент".
    2. Выберите файл веб-конфигурации из списка шаблонов и нажмите кнопку "Добавить".
  3. Измените <раздел connectionStrings> следующим образом:

    <asp:SqlDataSource ID="SqlDataSource1" runat="server"
        ConnectionString="<%$ConnectionStrings:Northwind%>"
        SelectCommand="SELECT * FROM Products">
    </asp:SqlDataSource>
    
  4. Перейдите в представление кода и добавьте атрибут ConnectionString и атрибут SelectCommand в <элемент управления asp:SqlDataSource> следующим образом:

    <asp:SqlDataSource ID="SqlDataSource1" runat="server"
        ConnectionString="<%$ConnectionStrings:Northwind%>"
        SelectCommand="SELECT * FROM Products">
    </asp:SqlDataSource>
    
  5. В режиме конструктора добавьте новый элемент управления GridView.

  6. В раскрывающемся списке "Выбор источника данных" в меню "Задачи GridView" выберите SqlDataSource1.

  7. Щелкните правой кнопкой мыши на Default.aspx и выберите опцию "Просмотр в браузере" в меню. Нажмите кнопку "Да", когда появится запрос на сохранение.

  8. GridView отображает данные из таблицы Products.

Упражнение 2. Редактирование данных с помощью элемента управления SqlDataSource

В следующем упражнении показано, как привязать элемент управления DropDownList с помощью декларативного синтаксиса и изменить данные, представленные в элементе управления DropDownList.

  1. В режиме конструктора удалите элемент управления GridView из Default.aspx.

    Важно: оставьте элемент управления SqlDataSource на странице.

  2. Добавьте элемент управления DropDownList в Default.aspx.

  3. Переключитесь в представление источника.

  4. Добавьте атрибут DataSourceId, DataTextField и DataValueField в <элемент управления asp:DropDownList> следующим образом:

    <asp:DropDownList ID="ddlProducts" runat="server"
         DataSourceId="SqlDataSource1" DataTextField="ProductName"
         DataValueField="ProductID">
    </asp:DropDownList>
    
  5. Сохраните Default.aspx и просмотрите его в браузере. Обратите внимание, что DropDownList содержит все продукты из базы данных Northwind.

  6. Закройте браузер.

  7. В режиме исходного кода Default.aspx добавьте новый элемент управления TextBox под элементом управления DropDownList. Измените свойство идентификатора textBox на txtProductName.

  8. В элементе управления TextBox добавьте новый элемент управления Button. Измените свойство ID кнопки на btnUpdate и установите свойство Text на Update Product Name.

  9. В режиме просмотра кода Default.aspx добавьте свойство UpdateCommand и два новых UpdateParameters в тег SqlDataSource следующим образом:

    <asp:SqlDataSource ID="SqlDataSource1" runat="server"
        ConnectionString="<%$ConnectionStrings:Northwind%>"
        SelectCommand="SELECT * FROM Products"
        UpdateCommand="UPDATE Products SET ProductName=@ProductName WHERE ProductID=@ProductID">
          <UpdateParameters>
          <asp:ControlParameter Name="ProductName" 
            ControlID="txtProductName" PropertyName="Text" />
          <asp:ControlParameter Name="ProductID" 
            ControlID="ddlProducts" PropertyName="SelectedValue" />
    </asp:SqlDataSource>
    

    Замечание

    Обратите внимание, что в этом коде добавлены два параметра обновления (ProductName и ProductID). Эти параметры сопоставляются со свойством Text текстового поля txtProductName и свойством SelectedValue выпадающего списка ddlProducts.

  10. Перейдите в конструктор и дважды щелкните элемент управления Button, чтобы добавить обработчик событий.

  11. Добавьте следующий код в код btnUpdate_Click:

    SqlDataSource1.Update();
    
  12. Щелкните правой кнопкой мыши Default.aspx и выберите "Просмотреть в браузере". Нажмите кнопку "Да", когда появится запрос на сохранение всех изменений.

  13. ASP.NET 2.0 частичные классы позволяют выполнять компиляцию во время выполнения. Для того, чтобы изменения кода вступили в силу, не нужно создавать приложение.

  14. Выберите продукт из DropDownList.

  15. Введите новое имя выбранного продукта в TextBox и нажмите кнопку "Обновить".

  16. Имя продукта обновляется в базе данных.

Упражнение 3. Использование элемента управления ObjectDataSource

В этом упражнении показано, как использовать элемент управления ObjectDataSource и исходный объект для взаимодействия с базой данных Northwind.

  1. Щелкните проект правой кнопкой мыши в обозревателе решений и щелкните "Добавить новый элемент".

  2. Выберите веб-форму в списке шаблонов. Измените имя на object.aspx и нажмите кнопку "Добавить".

  3. Щелкните проект правой кнопкой мыши в обозревателе решений и щелкните "Добавить новый элемент".

  4. Выберите класс в списке шаблонов. Измените имя класса на NorthwindData.cs и нажмите кнопку "Добавить".

  5. Нажмите кнопку "Да", когда появится запрос на добавление класса в папку App_Code.

  6. Добавьте следующий код в файл NorthwindData.cs:

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Data.SqlClient;
    public class NorthwindData {
        private string _connectionString;
        public NorthwindData() {
            Initialize();
        }
    
        private void Initialize() {
            if (ConfigurationManager.ConnectionStrings["Northwind"] == null ||
                ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString.Trim() == "") {
                    throw new Exception("A connection string named 'Northwind' with " +
                    "a valid connection string must exist in the <connectionStrings> " +
                    "configuration section for the application.");
            }
            _connectionString = ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
        }
    
        public DataTable GetAllEmployees(string sortColumns, int startRecord, int maxRecords) {
            VerifySortColumns(sortColumns);
            string sqlCmd = "SELECT EmployeeID, LastName, FirstName, Address, " +
                "City, Region, PostalCode FROM Employees ";
            if (sortColumns.Trim() == "")
                sqlCmd += "ORDER BY EmployeeID";
            else
                sqlCmd += "ORDER BY " + sortColumns;
    
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlDataAdapter da = new SqlDataAdapter(sqlCmd, conn);
            DataSet ds = new DataSet();
            try {
                conn.Open();
                da.Fill(ds, startRecord, maxRecords, "Employees");
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
            return ds.Tables["Employees"];
        }
    
        public int SelectCount() {
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM Employees", conn);
            int result = 0;
    
            try {
                conn.Open();
                result = (int)cmd.ExecuteScalar();
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
            return result;
        }
    
        //////////
        // Verify that only valid columns are specified in the sort expression to
        // avoid a SQL Injection attack.
        private void VerifySortColumns(string sortColumns) {
            if (sortColumns.ToLowerInvariant().EndsWith(" desc"))
                sortColumns = sortColumns.Substring(0, sortColumns.Length - 5);
            string[] columnNames = sortColumns.Split(',');
            foreach (string columnName in columnNames) {
                switch (columnName.Trim().ToLowerInvariant()) {
                    case "employeeid":
                        break;
                    case "lastname":
                        break;
                    case "firstname":
                        break;
                    case "":
                        break;
                    default:
                        throw new ArgumentException("SortColumns contains an " +
                            "invalid column name.");
                        break;
                }
            }
        }
    
        // Select an employee.
        public DataTable GetEmployee(int EmployeeID) {
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlDataAdapter da =
                new SqlDataAdapter("SELECT EmployeeID, LastName, FirstName, " +
                "Address, City, Region, PostalCode " +
                " FROM Employees WHERE EmployeeID = @EmployeeID", conn);
            da.SelectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID;
            DataSet ds = new DataSet();
            try {
                conn.Open();
                da.Fill(ds, "Employees");
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
    
            return ds.Tables["Employees"];
        }
    
        // Delete the Employee by ID.
        public int DeleteEmployee(int EmployeeID) {
             SqlConnection conn = new SqlConnection(_connectionString);
             SqlCommand cmd = new SqlCommand("DELETE FROM Employees WHERE " +
                 "EmployeeID = @EmployeeID", conn);
             cmd.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID;
             int result = 0;
             try {
                 conn.Open();
                 result = cmd.ExecuteNonQuery();
             } catch (SqlException e) {
                 // Handle exception.
             } finally {
                 conn.Close();
             }
    
             return result;
         }
    
         // Update the Employee by original ID.
         public int UpdateEmployee(int EmployeeID, string LastName, string FirstName,
             string Address, string City, string Region,
             string PostalCode) {
             if (String.IsNullOrEmpty(FirstName))
                 throw new ArgumentException("FirstName cannot be null or an empty string.");
             if (String.IsNullOrEmpty(LastName))
                 throw new ArgumentException("LastName cannot be null or an empty string.");
             if (Address == null) { Address = String.Empty; }
             if (City == null) { City = String.Empty; }
             if (Region == null) { Region = String.Empty; }
             if (PostalCode == null) { PostalCode = String.Empty; }
    
             SqlConnection conn = new SqlConnection(_connectionString);
             SqlCommand cmd = new SqlCommand("UPDATE Employees " +
                 " SET FirstName=@FirstName, " +
                 "LastName=@LastName, " +
                 "Address=@Address, City=@City, " +
                 "Region=@Region, " +
                 "PostalCode=@PostalCode " +
                 "WHERE EmployeeID=@EmployeeID", conn);
             cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName;
             cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName;
             cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address;
             cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City;
             cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region;
             cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode;
             cmd.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID;
    
             int result = 0;
             try {
                 conn.Open();
                 result = cmd.ExecuteNonQuery();
             } catch (SqlException e) {
                 // Handle exception.
             } finally {
                 conn.Close();
             }
    
             return result;
        }
    
        // Insert an Employee.
        public int InsertEmployee(string LastName, string FirstName,
            string Address, string City, string Region,
            string PostalCode) {
            if (String.IsNullOrEmpty(FirstName))
                throw new ArgumentException("FirstName cannot be null or an empty string.");
            if (String.IsNullOrEmpty(LastName))
                throw new ArgumentException("LastName cannot be null or an empty string.");
            if (Address == null) { Address = String.Empty; }
            if (City == null) { City = String.Empty; }
            if (Region == null) { Region = String.Empty; }
            if (PostalCode == null) { PostalCode = String.Empty; }
    
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlCommand cmd = new SqlCommand("INSERT INTO Employees " +
                " (FirstName, LastName, Address, " +
                " City, Region, PostalCode) " +
                " Values(@FirstName, @LastName, " +
                "@Address, @City, @Region, @PostalCode); " +
                "SELECT @EmployeeID = SCOPE_IDENTITY()", conn);
    
            cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName;
            cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName;
            cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address;
            cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City;
            cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region;
            cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode;
            SqlParameter p = cmd.Parameters.Add("@EmployeeID", SqlDbType.Int);
                p.Direction = ParameterDirection.Output;
            int newEmployeeID = 0;
            try {
                conn.Open();
                cmd.ExecuteNonQuery();
                newEmployeeID = (int)p.Value;
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
    
            return newEmployeeID;
        }
    
        //
        // Methods that support Optimistic Concurrency checks.
        //
        // Delete the Employee by ID.
        public int DeleteEmployee(int original_EmployeeID, string original_LastName,
            string original_FirstName, string original_Address,
            string original_City, string original_Region,
            string original_PostalCode) {
    
            if (String.IsNullOrEmpty(original_FirstName))
                throw new ArgumentException("FirstName cannot be null or an empty string.");
            if (String.IsNullOrEmpty(original_LastName))
                throw new ArgumentException("LastName cannot be null or an empty string.");
            if (original_Address == null) { original_Address = String.Empty; }
            if (original_City == null) { original_City = String.Empty; }
            if (original_Region == null) { original_Region = String.Empty; }
            if (original_PostalCode == null) { original_PostalCode = String.Empty; }
            string sqlCmd = "DELETE FROM Employees WHERE EmployeeID = " + @original_EmployeeID
    
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlCommand cmd = new SqlCommand(sqlCmd, conn);
            cmd.Parameters.Add("@original_EmployeeID",
                SqlDbType.Int).Value = original_EmployeeID;
            cmd.Parameters.Add("@original_FirstName",
                SqlDbType.VarChar, 10).Value = original_FirstName;
            cmd.Parameters.Add("@original_LastName",
                SqlDbType.VarChar, 20).Value = original_LastName;
            cmd.Parameters.Add("@original_Address",
                SqlDbType.VarChar, 60).Value = original_Address;
            cmd.Parameters.Add("@original_City",
                SqlDbType.VarChar, 15).Value = original_City;
            cmd.Parameters.Add("@original_Region",
                SqlDbType.VarChar, 15).Value = original_Region;
            cmd.Parameters.Add("@original_PostalCode",
                SqlDbType.VarChar, 10).Value = original_PostalCode;
    
            int result = 0;
            try {
                conn.Open();
                result = cmd.ExecuteNonQuery();
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
    
            return result;
        }
    
        // Update the Employee by original ID.
        public int UpdateEmployee(string LastName, string FirstName,
            string Address, string City, string Region,
            string PostalCode, int original_EmployeeID,
            string original_LastName, string original_FirstName,
            string original_Address, string original_City,
            string original_Region, string original_PostalCode) {
    
            if (String.IsNullOrEmpty(FirstName))
                throw new ArgumentException("FirstName cannot be null or an empty string.");
            if (String.IsNullOrEmpty(LastName))
                throw new ArgumentException("LastName cannot be null or an empty string.");
            if (Address == null) { Address = String.Empty; }
            if (City == null) { City = String.Empty; }
            if (Region == null) { Region = String.Empty; }
            if (PostalCode == null) { PostalCode = String.Empty; }
            if (original_Address == null) { original_Address = String.Empty; }
            if (original_City == null) { original_City = String.Empty; }
            if (original_Region == null) { original_Region = String.Empty; }
            if (original_PostalCode == null) { original_PostalCode = String.Empty; }
    
            string sqlCmd = "UPDATE Employees " +
                " SET FirstName = @FirstName, LastName = @LastName, " +
                " Address = @Address, City = @City, Region = @Region, " +
                " PostalCode = @PostalCode " +
                " WHERE EmployeeID = @original_EmployeeID";
    
            SqlConnection conn = new SqlConnection(_connectionString);
            SqlCommand cmd = new SqlCommand(sqlCmd, conn);
            cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName;
            cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName;
            cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address;
            cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City;
            cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region;
            cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode;
            cmd.Parameters.Add("@original_EmployeeID",
                SqlDbType.Int).Value = original_EmployeeID;
            cmd.Parameters.Add("@original_FirstName",
                SqlDbType.VarChar, 10).Value = original_FirstName;
            cmd.Parameters.Add("@original_LastName",
                SqlDbType.VarChar, 20).Value = original_LastName;
            cmd.Parameters.Add("@original_Address",
                SqlDbType.VarChar, 60).Value = original_Address;
            cmd.Parameters.Add("@original_City",
                SqlDbType.VarChar, 15).Value = original_City;
            cmd.Parameters.Add("@original_Region",
                SqlDbType.VarChar, 15).Value = original_Region;
            cmd.Parameters.Add("@original_PostalCode",
                SqlDbType.VarChar, 10).Value = original_PostalCode;
    
            int result = 0;
    
            try {
                conn.Open();
                result = cmd.ExecuteNonQuery();
            } catch (SqlException e) {
                // Handle exception.
            } finally {
                conn.Close();
            }
            return result;
        }
    }
    
  7. Добавьте следующий код в представление источника object.aspx:

    <%@ Page language="C#" %>
    <script RunAt="server">
    void EmployeesDetailsView_ItemInserted(Object sender, DetailsViewInsertedEventArgs e) {
        EmployeesGridView.DataBind();
    }
    
    void EmployeesDetailsView_ItemUpdated(Object sender, DetailsViewUpdatedEventArgs e) {
        EmployeesGridView.DataBind();
    }
    
    void EmployeesDetailsView_ItemDeleted(Object sender, DetailsViewDeletedEventArgs e) {
        EmployeesGridView.DataBind();
    }
    void EmployeesGridView_OnSelectedIndexChanged(object sender, EventArgs e) {
        EmployeeDetailsObjectDataSource.SelectParameters["EmployeeID"].DefaultValue =
            EmployeesGridView.SelectedDataKey.Value.ToString();
        EmployeesDetailsView.DataBind();
    }
    void EmployeeDetailsObjectDataSource_OnInserted(object sender,
        ObjectDataSourceStatusEventArgs e) {
    
        EmployeeDetailsObjectDataSource.SelectParameters["EmployeeID"].DefaultValue =
            e.ReturnValue.ToString();
        EmployeesDetailsView.DataBind();
    }
    void EmployeeDetailsObjectDataSource_OnUpdated(object sender,
        ObjectDataSourceStatusEventArgs e) {
    
        if ((int)e.ReturnValue == 0)
            Msg.Text = "Employee was not updated. Please try again.";
    }
    void EmployeeDetailsObjectDataSource_OnDeleted(object sender,
        ObjectDataSourceStatusEventArgs e) {
    
        if ((int)e.ReturnValue == 0)
            Msg.Text = "Employee was not deleted. Please try again.";
    }
    void Page_Load() {
        Msg.Text = "";
    }
    </script>
    <html>
      <body>
        <form id="Form1" runat="server">
          <h3>ObjectDataSource Example</h3>
          <asp:Label id="Msg" runat="server" ForeColor="Red" />
          <asp:ObjectDataSource
              ID="EmployeesObjectDataSource"
              runat="server"
              TypeName="NorthwindData"
              SortParameterName="SortColumns"
              EnablePaging="true"
              SelectCountMethod="SelectCount"
              StartRowIndexParameterName="StartRecord"
              MaximumRowsParameterName="MaxRecords"
              SelectMethod="GetAllEmployees" >
          </asp:ObjectDataSource>
          <asp:ObjectDataSource
              ID="EmployeeDetailsObjectDataSource"
              runat="server"
              TypeName="NorthwindData"
              ConflictDetection="CompareAllValues"
              OldValuesParameterFormatString="{0}"
              SelectMethod="GetEmployee"
              InsertMethod="InsertEmployee"
              UpdateMethod="UpdateEmployee"
              DeleteMethod="DeleteEmployee"
              OnInserted="EmployeeDetailsObjectDataSource_OnInserted"
              OnUpdated="EmployeeDetailsObjectDataSource_OnUpdated"
              OnDeleted="EmployeeDetailsObjectDataSource_OnDeleted">
              <SelectParameters>
                  <asp:Parameter Name="EmployeeID" Type="Int32" />
              </SelectParameters>
          </asp:ObjectDataSource>
          <table cellspacing="10">
            <tr>
              <td valign="top">
                <asp:GridView ID="EmployeesGridView"
                    DataSourceID="EmployeesObjectDataSource"
                    AutoGenerateColumns="false"
                    AllowSorting="true"
                    AllowPaging="true"
                    PageSize="5"
                    DataKeyNames="EmployeeID"
                    OnSelectedIndexChanged="EmployeesGridView_OnSelectedIndexChanged"
                    RunAt="server">
                    <HeaderStyle backcolor="lightblue" forecolor="black"/>
                    <Columns>
                    <asp:ButtonField Text="Details..."
                    HeaderText="Show Details"
                    CommandName="Select"/>
    
                    <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID"
                    SortExpression="EmployeeID" />
                    <asp:BoundField DataField="FirstName" HeaderText="First Name"
                    SortExpression="FirstName" />
                    <asp:BoundField DataField="LastName" HeaderText="Last Name"
                    SortExpression="LastName, FirstName" />
                    </Columns>
                </asp:GridView>
              </td>
              <td valign="top">
                <asp:DetailsView ID="EmployeesDetailsView"
                    DataSourceID="EmployeeDetailsObjectDataSource"
                    AutoGenerateRows="false"
                    EmptyDataText="No records."
                    DataKeyNames="EmployeeID"
                    Gridlines="Both"
                    AutoGenerateInsertButton="true"
                    AutoGenerateEditButton="true"
                    AutoGenerateDeleteButton="true"
                    OnItemInserted="EmployeesDetailsView_ItemInserted"
                    OnItemUpdated="EmployeesDetailsView_ItemUpdated"
                    OnItemDeleted="EmployeesDetailsView_ItemDeleted"
                    RunAt="server">
                    <HeaderStyle backcolor="Navy" forecolor="White"/>
                    <RowStyle backcolor="White"/>
                    <AlternatingRowStyle backcolor="LightGray"/>
                    <EditRowStyle backcolor="LightCyan"/>
                    <Fields>
                        <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID"
                            InsertVisible="False" ReadOnly="true"/>
                        <asp:BoundField DataField="FirstName" HeaderText="First Name"/>
                        <asp:BoundField DataField="LastName" HeaderText="Last Name"/>
                        <asp:BoundField DataField="Address" HeaderText="Address"/>
                        <asp:BoundField DataField="City" HeaderText="City"/>
                        <asp:BoundField DataField="Region" HeaderText="Region"/>
                        <asp:BoundField DataField="PostalCode" HeaderText="Postal Code"/>
                    </Fields>
                  </asp:DetailsView>
                </td>
              </tr>
            </table>
          </form>
        </body>
      </html>
    
  8. Сохраните все файлы и просмотрите object.aspx.

  9. Взаимодействуйте с интерфейсом, просматривая сведения, изменяя сотрудников, добавляя сотрудников и удаляя сотрудников.