Программирование в IIS

Веб-службы ASP.NET

Разбить на страницы
Показывать лекцию целиком

ASP.NET предлагает разработчику два основных типа проектов веб-приложения: веб-формы и веб-службы. Веб-формы обеспечивают логику представления при использовании бизнес-логики или логики данных. Веб-службы обеспечивают бизнес-логику в интернете и использование логики данных. Проект веб-службы не содержит элементы управления логики представления. Большинство веб-служб создаются для использования другими веб-службами, программами на альтернативных платформах или веб-формой перед получением пользователем данных, обработанных веб-службой.

Архитектура веб-служб

Служба ASP.NET состоит из библиотеки DLL, файла ASMX и файла Codebehind, который содержит класс, специально созданный для работы с веб-службами. Файл ASMX аналогичен файлу ASPX веб-формы ASP.NET, через него выполняются запросы веб-служб. В данной лекции мы будем считать веб-службу и класс с интегрированным HTTP-интерфейсом равнозначными. В проект можно добавлять другие классы, при этом не нужно создавать другой файл ASMX, так как классы сами по себе не являются веб-службами. При добавлении класса создается только файл для поддержки кода класса с именем <имя_класса>.cs или <имя класса>.<расширение_языка>.

В проект веб-службы Visual Studio .NET может входить несколько веб-служб. При компиляции проекта результат записывается в одну библиотеку DLL. Она включает классы нескольких файлов ASMX и дополнений к классам, поддерживающим файлы веб-службы ASMX.

Файл ASMX связан в IIS с расширением ISAPI aspnet_isapi.dll, поэтому при запросе файла ASMX из IIS aspnet_isapi.dll направляет HTTP-запрос нужной DLL-библиотеке веб-службы (см. рис. 3.1). Файл ASMX связан в один момент времени с одной DLL-библиотекой. Как правило, проекты веб-служб Visual Studio .NET располагают DLL в каталоге двоичных файлов внутри папки с файлами ASMX.

Веб-службы ASP.NET используют те же файлы конфигурации библиотеки и файлы конфигурации реализации IIS, что и веб-формы ASP.NET. Файлы веб-службы ASP.NET, используемые компилятором для создания веб-службы в библиотеке, именуются <имя_веб-службы>.asmx (в отличие от файлов веб-форм ASP.NET с расширением .aspx ). Веб-служба ASP.NET имеет ту же самую архитектуру, равно как и типы файлов, связанные с ней внутри проекта. Например, файл ASMX является адресуемой входной точкой веб-службы и как главный файл предоставляет директивы обработки для компиляции веб-службы. Файл ASMX описывает файлы, предназначенные для веб-службы ASP.NET, и сам может содержать в себе код. Код, связанный с веб-службой ASP.NET, обычно располагается в файле Codebehind. Файл Codebehind <моя_служба>.asmx.cs содержит исходный код веб-службы, а файл <моя_служба>.asmx.resx является источником, обслуживающим файл Codebehind.

(рис 3.1) Обзор архитектуры веб-служб

Примечание. Файл ASMX может включать код вместо файла Codebehind, однако Visual Studio .NET использует файл Codebehind в шаблоне проекта веб-службы по умолчанию. Если файл ASMX не использует Codebehind, то технология .NET компилирует файл и создает DLL автоматически по первому запросу.

Создание веб-службы с помощью Visual Studio .NET

Для демонстрации процесса создания веб-службы в данной лекции представлено простое приложение для составления расписания по датам. Рассматриваемая система расписания состоит из трех следующих классов.

  • anEvent. Класс представляет событие, происходящее в данный момент времени и день.
  • Events. Класс представляет коллективную группу экземпляров класса anEvent.
  • EventClient. Веб-форма, использующая экземпляры Events и anEvent.
  • Класс Events является веб-службой, класс anEvent – классом, используемым веб-службой Events, а класс EventClient – веб-формой, использующей классы anEvent и Events. Все классы располагаются в едином пространстве имен myPortal. В результате компиляции проекта создается библиотека myPortal.dll. Файл Events.asmx указывает на класс в библиотеке myPortal.dll. Библиотека myPortal.dll также содержит класс веб-формы EventClient и класс anEvent. Файл EventClient.aspx направляет технологию .NET в myPortal.dll как в скомпилированный код, содержащий соответствующий класс.

    Создадим новый проект веб-службы ASP.NET.

  • Откройте Visual Studio .NET. Отобразится начальная страница (если в Visual Studio .NET не отключено отображение начального окна). Как и в предыдущих версиях Visual Basic, предоставляется окно с шаблоном по умолчанию (вы можете его отключить).
  • Выберите File\New Project (Файл\Создать проект) для открытия окна со списком типов проектов и соответствующих шаблонов.
  • В списке слева выберите узел Visual C# Projects (Проекты Visual C#) – справа отобразятся шаблоны проектов для языка C#.
  • Выберите шаблон ASP.NET Web Service (Веб-служба ASP.NET).
  • Новой веб-службе в любом проекте по умолчанию присваивается имя Service1. В нашем примере проект называется myPortal (см. рис. 3.2). Задайте путь к корневому веб-каталогу сервера при помощи адреса URL или UNC. В URL указывается имя сервера по умолчанию localhost, если разрабатываемое программное решение располагается на рабочей станции при написании кода и модульном тестировании.
  • Нажмите на кнопку OK.
  • Visual Studio .NET выполнит подключение к веб-серверу, указанному в диалоговом окне New Project (Новый проект), создаст новый виртуальный каталог и скопирует в него файлы веб-служб по умолчанию.

    После генерации файлов проекта отобразится представление Design (Дизайн) веб-службы Service1, готовое к разработке. Visual Studio .NET присваивает веб-файлам веб-службы имена по умолчанию, поэтому их нужно переименовать.

  • Откройте Solution Explorer в меню View\Solution Explorer (Вид\Диспетчер программы).
  • Щелкните правой кнопкой мыши на файле Service1.asmx и выберите Rename (Переименовать).
  • Удалите имя Service1 и введите имя Events.
  • Нажмите на клавишу Enter, и имена файлов ASMX, Codebehind и файла разрешения сменятся на Events.asmx, Events.asmx.cs и Events.asmx.res соответственно.
  • Класс, обеспечивающий работу веб-службы, будет по-прежнему называться Service1. Веб-служба исправно функционирует с различными именами, но мы все-таки сменим имя класса, присвоив ему имя службы.

  • Выделите файл Events.asmx в диспетчере Solution Explorer и нажмите на кнопку View Designer (Отобразить дизайнер). Файл Events.asmx.cs отобразится в дизайнере компонентов).
  • Щелкните правой кнопкой мыши на файле Events.asmx.cs и выберите Properties (Свойства).
  • В открывшемся окне свойство Name (Имя) содержит значение Service1. Удалите это значение.
  • Введите в свойстве имени значение Events. Дизайнер компонентов автоматически изменит каждый экземпляр имени класса в файле Codebehind.
  • Веб-служба Events выполняет добавление, открытие или удаление экземпляров класса anEvent из портала. Данные экземпляра anEvent сохраняются в базе данных.

    (рис 3.2) Новый проект myPortal, создаваемый в диалоговом окне New Project (Новый проект)

    Класс Events взаимодействует с базой данных, поэтому при построении веб-службы мы используем компоненты инструментария, реализующие получение и отправку (см. рис. 3.3). Компоненты добавятся в представление Design (Дизайн) веб-службы Events.

    (рис 3.3) Компоненты дизайнера форм, добавленные в представлении Design (Дизайн) веб-службы Events

    Работа с дизайнером компонентов

    Инструментарий предоставляет множество компонентов для добавления в веб-службу. Некоторые из них доступны как в веб-формах, так и в веб-службах, однако в проекте веб-службы недоступны компоненты, предназначенные для создания графического интерфейса для пользователя. Добавление компонентов веб-службы в представлении Design (Дизайн) не влияет на файл Events.asmx. ASMX аналогичен файлу веб-формы ASPX, так как связывает язык, класс, файл Codebehind и файл источника с веб-службой. Отличие заключается в том, что ASMX не содержит другой информации, предназначенной для отображения.

    Содержимое файла Events.asmx приведено в листинге 3.1. Если бы этот код предназначался для веб-формы, он содержал бы больший объем кода XML и HTML, определяющего параметры отображения; но он предназначен для веб-службы и содержит лишь одну строку текста.

    <%@ WebService Language="c#" Codebehind="Events.asmx.cs" 
    Class="myPortal.Events" %>

    При добавлении компонента подключения (или любого другого) в представление Design (Дизайн) в окне свойств настраиваются параметры этого компонента. Для каждого значения, устанавливаемого в этом окне, дизайнер компонентов генерирует код инициализации в файле Codebehind веб-службы и определяет предпроцессорную команду. Предпроцессорные команды в C# аналогичны командам в C++ и C, отличие же заключается в следующем.

  • Предпроцессорные команды языка C# не интерпретируются предпроцессором, отдельным от компилятора. Компилятор и предпроцессор представляют единое целое.
  • Команды предпроцессора C# не могут указывать макросы.
  • Команда #region указывает область, расширяемую или сужаемую в редакторе Visual Studio .NET. Команда #region не оказывает функционального влияния на программное решение.

    В коде, сгенерированном дизайнером компонентов, Visual Studio. NET объявляет три элемента:

  • ссылку на Icontainer с именем components ;
  • подпрограмму InitializeComponent ;
  • функцию Dispose.
  • Все компоненты, добавленные в представлении Design (Дизайн), инициализируются в подпрограмме InitializeComponent. Вызов подпрограммы InitializeComponent автоматически размещается в конструкторе веб-службы. Все свойства, устанавливаемые в окне свойств (см. рис. 3.3), присваиваются компоненту, которому они принадлежат. Комментарии, размещаемые дизайнером компонентов в данной области, предупреждают о том, что не следует изменять код вручную. Область кода, сгенерированного дизайнером компонентов, отображается в виде секции, взаимодействующей с представлением Design (Дизайн) веб-службы.

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

    Настройка компонента подключения к базе данных

    Источником данных для веб-службы Events является база данных SQL Server 2000, поэтому добавим компонент SQLConnection. При поддержке другого типа базы данных используется компонент OleDBConnection. Компонент SQLConnection дает дополнительные возможности по управлению и повышает эффективности работы, в отличие от компонента OleDBConnection, поэтому последний используется только в случае необходимости. Если для источника данных отсутствует провайдер OLE-DB, то загрузите с сайта Microsoft доступен провайдер ODBC, поставляемый в отдельной библиотеке.

    Свойство ConnectionString в окне свойств позволяет выбрать предыдущие подключения, настроенные на рабочей станции, или создать новое подключение. Для нашего примера создадим новое подключение (см. рис. 3.4).

    (рис 3.4) Выбор нового подключения в свойстве ConnectionString компонента SQLConnection

    После выбора <New Connection...> в качестве параметра свойства ConnectionString откроется окно Data Link Properties (Свойства подключения к данным). В этом окне настраиваются все параметры, представленные в строке подключения ADO.NET. Окно свойств подключения к данным – это общее окно Visual Studio для настройки подключения ADO. Для веб-службы Events используется сервер базы данных, расположенный на сервере AMD1700, и база данных ASPNETServices (см. рис. 3.5).

    (рис 3.5) Настройка нового подключения к данным для веб-службы Events

    Изменим имя компонента по умолчанию SQLConnection1 на более дружественное пользователю ServicesDBConn. После настройки и создания подключение SQLConnection работает в любом месте веб-службы. Класс Events наследуется из System.Web.Services. Класс WebService содержит множество классов и функций для взаимодействия с HTTP-соединением. В листинге 3.2 приведен исходный код, сгенерированный Visual Studio .NET для класса Events.

    /// <summary>
    /// Summary description for Events.
    /// </summary>
    public class Events : System.Web.Services.WebService
    {
         public Events()
         {
              //CODEGEN: This call is required by the 
              //ASP.NET Web Services Designer
              InitializeComponent();
         }
    
         private System.Data.SqlClient.SqlConnection ServicesDBConn;
    
         #region Component Designer generated code
         
         //Required by the Web Services Designer
         private IContainer components = null;
                   
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
         {
              this.ServicesDBConn = new 
                                 System.Data.SqlClient.SqlConnection();
              //
              // ServicesDBConn
              //
              this.ServicesDBConn.ConnectionString = 
              "data source=amd1700;initial catalog=ASPNETServices;" +
              "integrated security=SSPI;persist security info=False;" +
              "workstation id=AMD1700;packet size=4096";
    
         }
    
         /// <summary>
         /// Clean up any resources being used.
         /// </summary>
         protected override void Dispose( bool disposing )
         {
              if(disposing  components != null)
              {
                   components.Dispose();
              }
              base.Dispose(disposing);
         }
         
         #endregion
    }

    При работе с элементами управления веб-службы использование дизайнера компонентов не обязательно. Элементы управления добавляются в веб-службу посредством объявления и конструирования в файле Codebehind, как и другие переменные и классы. Например, экземпляр объекта SQLConnection можно создать и инициализировать в конструкторе класса Events. Код, приведенный в листинге 3.3, демонстрирует создание локального экземпляра объекта SQLConnection без использования дизайнера компонентов и компонента SQLConnection.

    public class Events : System.Web.Services.WebService
    {
         //locals to class
         private System.Data.SqlClient.SqlConnection myConn;
    
         public Events()
         {
              //CODEGEN: This call is required by the 
              //ASP.NET Web Services Designer
              InitializeComponent();
    
              System.Configuration.AppSettingsReader
                   myAppSettings =
                   new System.Configuration.AppSettingsReader();
    
              //get the connection string from web.config
              string sConnect =
                   ((string)
                   (myAppSettings.GetValue
                   ("ProductionDB.ConnectionString",
                   typeof(string))));
    
              //make the DB connection
            myConn = new System.Data.SqlClient.SqlConnection(sConnect);
         }
    
         #region Component Designer generated code
         
         //Required by the Web Services Designer
         private IContainer components = null;
                   
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
         {
         }
         /// <summary>
         /// Clean up any resources being used.
         /// </summary>
         protected override void Dispose( bool disposing )
         {
              if(disposing  components != null)
              {
                   components.Dispose();
              }
              base.Dispose(disposing); 
         }
         
         #endregion
    }

    В листинге 3.3 экземпляр myConn объявляется локально по отношению к классу Events. Строка подключения извлекается из файла web.config и используется в качестве конструктора экземпляра myConn. После создания экземпляра myConn в конструкторе Events его можно использовать в любом месте класса.

    Считывание параметров приложения из web.config

    Файл web.config великолепно подходит для получения инициализационных данных, индивидуальных для веб-приложения. Это позволяет создавать решение программно, чтобы получить аргументы, связанных с конкретной реализацией, без внесения изменений в код других реализаций. С помощью класса AppSettingsReader осуществляется считывание данных из секции <appSettings> файла web.config. В листинге 3.4 приведен файл web.config, содержащий строку подключения к базе данных, полученную в листинге 3.3 в конструкторе Events.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>   
      <system.web>
        <compilation defaultLanguage="c#" debug="true"/>
        <customErrors mode="RemoteOnly" />
    </system.web>
    
     <appSettings>
        <add key="ProductionDB.ConnectionString"
              value="data source=amd1700;
                     initial catalog=ASPNETServices;
                     integrated security=SSPI;
                     persist security info=False;
                     workstation id=AMD1700;
                     packet size=4096" 
         />
      </appSettings>
    
    </configuration>

    Дизайнер компонентов содержит механизм, использующий элемент appSettings файла web.config. В окне свойств компонента подключения в представлении Design (Дизайн) рассматриваемой веб-службы можно настроить параметр ConnectionString как набор динамических свойств DynamicProperties (см. рис.3.6).

    (рис 3.6) Свойства компонента подключения, использующие DynamicProperties для получения строки подключения ConnectionString

    По умолчанию свойство ConnectionString не настроено на использование DynamicProperties. Каждое свойство нужно связать с ключом в файле web.config. Ключ представляет собой атрибут с именем key, расположенный в элементе add, подчиненном элементу appSettings (см. рис. 3.4). При нажатии на кнопку "..." в свойстве ConnectionString откроется диалоговое окно для выбора ключа (см. рис. 3.7).

    (рис 3.7) Связывание свойства ConnectionString с ключом в файле конфигурации

    Компонент Data Adaptor

    После установки подключения веб-служба узнает источник данных и аутентификационные данные, необходимые для предоставления этому источнику. Далее веб-служба отправляет запрос источнику данных на получение данных. ADO.NET предлагает для этого еще один компонент – адаптер данных (data adaptor). Версия адаптера данных для SQL Server 2000 использует провайдер SQL Server .NET с именем SQLDataAdaptor. OledbDataAdaptor (версия для OLE-DB) работает с предыдущими версиями SQL Server или базами данных, не имеющих конкретного провайдера. Адаптер данных можно установить, настроить и применить, как и любой другой класс в ADO.NET. ASP.NET содержит в инструментарии альтернативный компонент, который можно использовать в случае необходимости. Адаптер данных добавляется в дизайнере компонентов веб-службы и настраивается в окне свойств.

    При добавлении компонента открывается окно мастера настройки адаптера данных (Data Adaptor Configuration Wizard). Это окно при желании можно закрыть, а свойства настроить вручную.

    Ниже приведены инструкции по работе с мастером настройки адаптера данных.

  • В стартовом окне нажмите на кнопку Next (Далее), чтобы начать настройку адаптера данных, добавленного в веб-службу.
  • В окне Choose Your Data Connection (Выбор подключения к данным) (см. рис. 3.8) в ниспадающем списке выберите подключение к данным, настроенное для проекта. При нажатии на кнопку New Connection (Создать подключение) откроется окно Data Link Properties (Свойства подключения к данным) (см. рис. 3.5) для настройки соединения. В нашем примере выберите ранее созданное подключение.(рис 3.8) Мастер настройки адаптера данных: выбор подключения к данным
  • После выбора подключения нажмите на кнопку Next (Далее), и мастер предложит выбрать метод запроса данных из источника в окне Choose A Query Type (Выбор тип запроса) (см. рис. 3.9).

    (рис 3.9) Мастер настройки адаптера данных: выбор типа запроса

    Выберите Use SQL Statements (Использовать операторы SQL), и мастер сгенерирует набор командных объектов (с помощью класса SQLCommand или OledbCommand ) в функции InitializeComponent класса веб-службы. Командные объекты будут созданы с набором SQL-команд, которые используют псевдонимы каждого имени поля. С помощью ссылки на псевдоним в экземпляре командного объекта можно установить значения рассматриваемого поля.

    Выберите Create New Stored Procedures (Создать новые сохраненные процедуры) или Use Existing Stored Procedures (Использовать имеющиеся сохраненные процедуры), и мастер запросит дополнительную информацию о таблицах данных, необходимую для построения и связи сохраненных процедур.

  • В нашем примере выберите опцию Use SQL Statements.
  • Нажмите на кнопку Next (Далее) для перехода в окно Generate The SQL Statements (Генерация выражений SQL). Введите команду Select SQL для набора данных, используемых адаптером при построении выражений SQL (см. рис. 3.10). SQLDataAdaptor и OledbDataAdaptor поддерживают интерфейс для выполнения операторов Select (Выбор), Insert (Вставка), Delete (Удаление) и Update (Обновление). Мастер генерирует команды SQL для управления данными, устанавливаемыми в адаптере данных. Он запрашивает выражение SQL Select для создания текста команд Select, Insert, Delete и (рис 3.10) Мастер настройки адаптера данных: генерация выражений SQL
  • После ввода в поле оператора Select теста select * from tblEvents нажмите на кнопку Next (Далее), чтобы отобразить окно подтверждения View Wizard Results (Просмотр результатов работы мастера) (см. рис. 3.11). В этом окне описываются действия, выполненные с кодом веб-службы. При возникновении проблем с анализом выражения SQL, введенного в предыдущем шаге, в этом окне вы не увидите все возможные комментарии SQL, показанные на рис. 3.11. Нажмите на кнопку Back (Назад) и повторите введенную команду SQL или выберите создание сохраненных процедур на сервере базы данных.
  • (рис 3.11) Мастер настройки адаптера данных: просмотр результатов работы

    В нашем примере адаптер данных называется SQLDataAdaptor1, так как дизайнер компонентов присваивает это имя по умолчанию любому компоненту SQLDataAdaptor, добавляемому в представлении Design (Дизайн). Это имя можно изменить в любой момент, и сгенерированный мастером код в подпрограмме InitializeComponent веб-службы автоматически обновится. В нашем примере имя SQLDataAdaptor изменено на EventsAdaptor в окне свойств.

    Объектам SQLCommand, сгенерированным мастером настройки адаптера данных, присвоены имена SQLSelectCommand1, SQLDeleteCommand1, SQLInsertCommand1 и SQLUpdateCommand1. Имена командных объектов заменены на SelectEvent, DeleteEvent, InsertEvent и UpdateEvent соответственно. Это было сделано в окне свойств SQLDataAdaptor, т.е. изменено свойство имени, подчиненное динамическим свойствам Dynamic Properties каждого командного объекта. В листинге 3.5 приведен код, сгенерированный дизайнером компонентов для адаптера данных EventsAdaptor и объекта подключения к данным ServicesDBConn.

    private void InitializeComponent()
    {
         System.Configuration.AppSettingsReader 
         configurationAppSettings = new 
         System.Configuration.AppSettingsReader();
         
       this.ServicesDBConn = new System.Data.SqlClient.SqlConnection();
    
       this.EventsAdaptor = new System.Data.SqlClient.SqlDataAdapter();
    
       this.SelectEvent = new System.Data.SqlClient.SqlCommand();
    
       this.InsertEvent = new System.Data.SqlClient.SqlCommand();
    
       this.UpdateEvent = new System.Data.SqlClient.SqlCommand();
    
       this.DeleteEvent = new System.Data.SqlClient.SqlCommand();
    
         //
         // ServicesDBConn
         //
          this.ServicesDBConn.ConnectionString = 
                            ((string)(configurationAppSettings.GetValue
                 ("ServicesDBConn.ConnectionString", typeof(string))));
    
         //
         // EventsAdaptor
         //
         this.EventsAdaptor.DeleteCommand = this.DeleteEvent;
    
         this.EventsAdaptor.InsertCommand = this.InsertEvent;
    
         this.EventsAdaptor.SelectCommand = this.SelectEvent;

    В листинге 3.5 приведена часть подпрограммы InitializeComponent, демонстрирующая компоненты SQLCommand, созданные дизайнером компонентов после переименования. После создания класса AppSettingsReader для получения параметров приложения из файла web.config созданы классы подключения, адаптера данных и команд. После инициализации класса подключения посредством строки подключения, полученной из файла web.config, класс адаптера данных присваивает каждый экземпляр объекта SQLCommand соответствующему свойству команды. Например, экземпляр команды DeleteEvent присваивается свойству DeleteCommand экземпляра SQLDataAdaptor с именем EventsAdaptor.

    Дизайнер компонентов сгенерировал достаточно большой объем кода для настройки текста команд SQL и параметров каждого экземпляра командных объектов (это не отражено в листинге 3.5). Он определил каждое поле в наборе данных посредством псевдонима. Псевдонимы в командном объекте были установлены в качестве параметров. В листинге 3.6 приведен исходный код, реализующий текст команды SQL и параметры командного объекта InsertEvent.

    //
    // InsertEvent
    //
    this.InsertEvent.CommandText = "INSERT INTO tblEvent" + 
               "(Name, StartDate, Description) " + 
              "VALUES (@Name, @StartDate, @Description); "+ 
              "SELECT Name, StartDate, Description, ID " + 
              "FROM tblEvent WHERE (ID = @@IDENTITY)";
    
    this.InsertEvent.Connection = this.ServicesDBConn;
    
    this.InsertEvent.Parameters.Add(
                                new System.Data.SqlClient.SqlParameter
                 ("@Name", System.Data.SqlDbType.VarChar, 50, "Name"));
    
    this.InsertEvent.Parameters.Add(
                                new System.Data.SqlClient.SqlParameter
                        ("@StartDate", System.Data.SqlDbType.DateTime, 
                           8, "StartDate"));
    
    this.InsertEvent.Parameters.Add(
                                 new System.Data.SqlClient.SqlParameter
                        ("@Description", System.Data.SqlDbType.VarChar, 
                                                  200, "Description"));

    Класс Data Reader

    С учетом псевдонимов, сгенерированных дизайнером компонентов, можно разработать код, устанавливающий параметры и заполняющий объект DataReader. Например, функция Add веб-службы Events использует экземпляр класса команды InsertEvent для добавления нового события. Псевдонимы имен полей таблицы базы данных, определенные при инициализации класса InsertEvent, используются методом Parameters для присвоения значения полю, представляемому псевдонимом в команде Insert SQL, используемой классом InsertEvent для обновления таблицы базы данных. В листинге 3.7 приведен код функции Add.

    /// <summary>
    /// Adds a new event to system
    /// </summary>
    [WebMethod]
    public bool Add()
    {
         System.Data.SqlClient.SqlDataReader myEvent;
    
     try
     {
      //set state for the command object instance
      InsertEvent.Parameters["@Name"].Value =mEvent.Name;
    
      InsertEvent.Parameters["@StartDate"].Value =mEvent.DateTime;
    
      InsertEvent.Parameters["@Description"].Value =mEvent.Description;
    
       //open the connection object
      ServicesDBConn.Open();
    
      //run query
      myEvent = this.InsertEvent.ExecuteReader();
       
      //must advance to first record
      myEvent.Read();
      
      //fill local properties
      mEvent.ID =  myEvent.GetInt32(3);
    
      //shut down record
      myEvent.Close();
     }
     catch(System.Exception Err )
     {
      //log error
      this.LogMessage(Err.ToString() , true);
      
      //rethrow for client consumption
      throw Err;
     }
    
      //success if we get this far
      return true;
    }

    Экземпляр класса SQLDataReader создан для доступа к данным, возвращаемым из экземпляра класса SQLCommand с именем InsertEvent. Экземпляр InsertEvent установлен в качестве свойства объекта SQLDataAdaptor EventsAdaptor и имеет объект подключения servicesDBConn, поэтому он возвращает данные экземпляру объекта SQLDataReader с именем myEvent. Локальный экземпляр anEvent с именем mEvent используется для хранения текущего состояния события веб-службы Events. В листинге 3.8 приведен исходный код класса anEvent.

    /// <summary>
    /// a single event
    /// </summary>
    public class anEvent
    {
    
         private long CurrentID;
         private string CurrentName;
         private string CurrentDescription;
         private DateTime CurrentDateTime;
    
         public anEvent()
         {
         }
    
         /// <summary>
         /// ID for opened event
         /// </summary>
         public long ID
         {
              get
              {
                   return CurrentID;
              }
              set
              {
                   CurrentID = value;
              }
         }
    
         /// <summary>
         /// Date and time of actual event
         /// </summary>
         public DateTime DateTime
         {
              get
              {
                   return CurrentDateTime;
              }
              set
              {
                   CurrentDateTime = value;
              }
         }
    
         /// <summary>
         /// description of event
         /// </summary>
         public string Description
         {
              get
              {
                   return CurrentDescription;
              }
              set
              {
                   CurrentDescription = value;
              }
         }
    
         /// <summary>
         /// Name for opened event
         /// </summary>
         public string Name
         {
              get
              {
                   return CurrentName;
              }
              set
              {
                   CurrentName = value;
              }
         }
    }

    Как видно из листинга 3.8, класс anEvent содержит данные, формирующие событие Event. Веб-служба Events считывает и записывает значения события в источник базы данных, поэтому для класса Events объявлен и создан локальный экземпляр anEvent.

    Когда функция Add (см. листинг 3.7) устанавливает параметры для Name, Date и Description, она берет соответствующие значения свойств для экземпляра mEvent класса anEvents. Свойства mEvent устанавливаются в веб-службе Events потребителем или извлекаются из базы данных для Event, загружаемой в веб-службу. После установки параметров команды InsertEvent соединение открывается, команда выполняется и происходит считывание результатов в экземпляре myEvent с помощью команды ExecuteReader объекта SQLCommand InsertEvent. Команда Read объекта SQLDataReader выполняет переход к следующей строке набора данных, представляющей первую строку во вновь открытом наборе данных. Если считывание myEvent происходит без перехода к первой строке, в ADO.NET возникает ошибка. Локальный экземпляр anEvent обновляется новым ID, так как база данных генерирует ID для конкретного класса anEvent.

    Многие программисты, знакомые с версиями ADO, предшествовавшими технологии ADO.NET, удивятся тому, как используется порядковая позиция поля ID, на которую ссылаются для получения значения ID экземпляра mEvent. Порядковое значение любого поля не должно использоваться для обращения к значению в наборе записей. SQLDataReader похож на набор записей ADO. Если таблица базы данных обновляется с добавлением нового столбца, то набор записей ADO может записать неправильное значение поля. Это зависит от того, в каком месте таблицы добавлен новый столбец. Однако в технологии ADO.NET этой проблемы не существует, так как порядковая позиция определяется в терминах экземпляра адаптера данных. Адаптер данных обеспечивает определенный уровень абстрагирования от уровня данных, поэтому программист может обращаться к порядковой позиции, не опасаясь, что изменения в таблице базы данных повредят программное обеспечение.

    Компонент Event Log

    Весь код взаимодействия с базой данных в листинге 3.7 расположен в секции Try блока Try .. Catch. Если удовлетворяется условие исключения, то выполняется блок Catch. Экземпляр класса System.Exception передается в качестве аргумента функции Catch и содержит информацию об ошибке, возникшей в блоке Try. В листинге 3.7 экземпляр класса System.Exception упорядочен в строку с помощью функции ToString. Функция ToString представляет собой общую функцию, используемую со многими классами, так как она унаследована из класса object. Строковое представление сообщения об ошибке передается функции LogMessage, которая записывает ошибку в журнал Application Log в программе Event Viewer (Просмотр событий), после чего ошибка передается приложению-потребителю.

    Функция LogMessage реализована таким образом, что в журнал Application несущего сервера можно записать любую информацию. Журнал Application поддерживает способ идентификации типа записываемого сообщения. Функция LogMessage записывает сообщения типа error или information в зависимости от переданного значения параметра Error. Если значение параметра Error равняется "истине", LogMessage идентифицирует сообщение в журнале Application как error (ошибка); в противном случае сообщение определяется как information (сведения). В листинге 3.9 приведен код функции LogMessage.

    /// <summary>
    /// logs messages to event log
    /// in: string that contains message of error
    ///          or information that ought to be logged
    ///          
    ///     out: returns true if successful write,
    ///          rethrows the error if failure occurs
    /// </summary>
    ///
    [WebMethod]
    public bool LogMessage(string Message, bool Error)
    {
         System.Diagnostics.EventLogEntryType MessageType;
    
         try
         {
              //determine the type of message
              if (Error )
              {
                   MessageType =
                        System.Diagnostics.EventLogEntryType.Error;
              }
              else
              {
                   MessageType =
                   System.Diagnostics.EventLogEntryType.Information;
              }                    
              
              //make the write
              this.Log.WriteEntry(Message, MessageType);
         }
         catch (System.Exception eLogWrite)
         {
              //nothing else left to do except throw the raw error
              throw eLogWrite;
         }               
         //we have success if we get to this line
         return true;
    }

    Экземпляр объекта Log является журналом приложения несущего сервера. Дизайнер компонентов содержит компонент, представляющий журнал несущего сервера в программе Event Viewer (Просмотр событий). Просто вставьте элемент управления в представлении Design (Дизайн) веб-службы, после чего настройте свойства журнала на том узле, куда будут передаваться сообщения. В данном случае подходит журнал Application (Приложение), так как он фиксирует информацию о приложении.

    Фиксирование данных о состоянии и ошибок в журнале

    Во всех веб-службах на локальном сервере должны фиксироваться данные о состоянии и ошибках. Довольно часто в веб-проектах не предусматривается адекватная система обработки ошибок или предоставление диагностической информации о том, какие действия выполняются в коде. Если в приложении возникают ошибки, а ошибочное условие нельзя продублировать в среде разработки, то выход из положения – разрешить разработчику доступ к среде разработки для диагностирования и отладки кода. Приложение не должно диагностироваться в среде разработки. Поскольку разработчики могут просматривать сообщения, записанные в журнал приложения, и использовать простые методы записи информации в журнал Application, нет причин для выполнения подобных действий при диагностике ошибки. Разрешение доступа к среде разработки является большой ошибкой приложения, и ее легко избежать, обеспечив надежную систему обработки ошибок и ведение журналов диагностики.

    Безопасность приложения

    Запись данных в журнал событий не требует модификации прав, под которыми работает веб-служба. Новыми возможностями IIS6 являются две встроенные учетные записи для рабочих процессов: Network Service (Сетевая служба) и Local Service (Локальная служба). В предыдущих версиях рабочие процессы для IIS выполнялись под учетной записью Local System (Локальная система). Учетная запись Network Service (Сетевая служба) является учетной записью по умолчанию для выполнения рабочих процессов в IIS. Она более ограничительна по сравнению с Local System (Локальная система), а учетная запись Local Service (Локальная службы) – более ограничительна по сравнению с Network Service (Сетевая служба). Можно создать особую учетную запись для обеспечения определенных требований к безопасности приложения.

    Для настройки приложения на работу под другой учетной записью следует создать новый пул приложения и настроить его на работу под аутентификационными данными учетной записи Local System (Локальная система).

  • Откройте консоль MMC Computer Management (Управление компьютером) с помощью команды Start\Administrative Tools (Пуск\Администрирование).
  • Откройте узел Internet Information Services (IIS) Manager (Диспетчер IIS), затем – узел папки Aplication Pools (Пулы приложения). В дереве, расположенном под папкой Application Pools (Пулы приложений), в виде значков отобразятся все пулы приложения. В списке должен присутствовать пул приложения DefaultAppPool. При его развертывании отобразятся веб-сайты, использующие этот элемент (см. рис. 3.12).
  • Веб-сайт по умолчанию Default Web Site, созданный при установке IIS, использует пул DefaultAppPool. Как видно из рисунка 3.12, сайт Default Web Site присутствует в списке многих веб-приложений, использующих DefaultAppPool. Для настройки пула приложения откройте страницу свойств веб-сайта или виртуального каталога, щелкнув правой кнопкой мыши на узле и выбрав команду Properties (Свойства).(рис 3.12) Приложения, использующие DefaultAppPool, отображаются в консоли MMC Computer Management (Управление компьютером)
  • Откройте вкладку Home Directory (Домашний каталог) веб-сайта либо вкладку Virutal Directory (Виртуальный каталог) виртуального каталога. Во вкладке Home Directory (Домашний каталог) поле со списком Application Pool (Пул приложения) отображает доступные пулы приложения (см. рис. 3.13).

    (рис 3.13) Вкладка Home Directory (Домашний каталог) окна свойств веб-сайта по умолчанию

    Веб-служба Events работает в виртуальном каталоге myPortal, расположенном на сайте Default Web Site. Виртуальный каталог myPortal настроен на использование пула DefaultAppPool, работающего под аутентификационными данными учетной записи Network Service (Сетевая служба). Когда веб-служба Events попытается записать данные в журнал событий, произойдет ошибка, так как учетная запись Network Service (Сетевая служба) не имеет соответствующих прав. В предыдущей версии IIS запись в журнал приложений веб-службой Events осуществляется без всяких проблем. Для разрешения проблемы доступа создайте новый пул приложения, использующий учетную запись Local System, и настройте myPortal на работу с новым пулом приложения.

  • Щелкните правой кнопкой мыши на значке Application Pools (Пулы приложений) в консоли Computer Management (Управление компьютером) и выберите New\Aplication Pool (Создать\Пул приложения).
  • В диалоговом окне Add New Application Pool (Создание нового пула приложения) (см. рис. 3.14) видно, что идентификатор ID пула приложения равен значению webservice using LocalSystem. В нашем случае используются параметры по умолчанию нового пула приложения.(рис 3.14) Диалоговое окно Add New Application Pool (Создание нового пула приложения)
  • Нажмите на кнопку OK.
  • Ниже приведены шаги по смене объекта пула приложения webservice using LocalSystem.

  • Щелкнув правой кнопкой на вновь созданном узле webservice using LocalSystem в консоли MMC Computer Management (Управление компьютером) в узле Application Pools (Пулы приложения) и выберите пункт Properties (Свойства).
  • В окне свойств откройте вкладку Identity (Объект) и в списке учетных записей Predefined security account (Предопределенные учетные записи безопасности) выберите Local System (Локальная система) (см. рис. 3.15).(рис 3.15) Настройка учетной записи для пула приложения
  • Нажмите на кнопку OK или на кнопку Apply (Применить). Появится окно с предупреждением об опасностях, связанных с работой пула приложений в учетной записи Local System (Локальная система). Вы можете настроить учетную запись в соответствии с требованиями безопасности, чтобы веб-служба осуществляла запись в журнал событий приложения.
  • После создания пула приложения виртуальный каталог myPortal нужно настроить на использование пула приложения webservice using LocalSystem. Откройте окно свойств для myPortal и во вкладке Directory (Каталог) выберите новый пул приложения (см. рис. 3.16).

    (рис 3.16) Выбор нового пула приложения webservice using LocalSystem для myPortal

    Тестирование веб-службы

    Помимо метода Add (см. листинг 3.7) и функции LogMessage (см. листинг 3.9) служба Events содержит три общих веб-метода: Delete, Open и HelloWorld. Методы Delete и Open аналогичны функции Add, т.е. выполняют чтение и запись отправляемых пользователем данных. HelloWorld является методом по умолчанию, размещаемым дизайнером компонентов во всех новых веб-службах для демонстрации создания метода, доступного из интернета.

    Если файл ASMX веб-службы установлен в качестве стартового файла, веб-службу можно запускать и отлаживать в Visual Studio .NET. Нажмите на клавишу F5 для запуска компилятора; при отсутствии ошибок откроется браузер с отображением тестовой структуры технологии .NET (см. рис. 3.17).

    Щелкните на любом из вызовов метода для открытия новой страницы, отображающей форму записи данных на базе параметров метода. Для описываемого метода под полями записи данных определены и описаны запрос и ответ HTTP и SOAP.

    (рис 3.17) Запрос веб-службы с помощью браузера

    Данные, расположенные в текстовых полях на странице тестирования, будут переданы в качестве аргументов параметров метода после нажатия на кнопку Invoke (Запросить). На рисунке 3.18 показана страница тестирования метода Open в браузере.

    (рис 3.18) Метод Open веб-службы Events, запрошенный в браузере

    Присвойте ID значение 13 и нажмите на кнопку Invoke (Запросить), в результате чего откроется другое окно браузера с результатом вызова метода Open в формате XML. Метод Open возвращает значение "истина" или "ложь" в случае успеха или неудачи (соответственно) выполняемой функции, поэтому такой ответ неинформативен. Он не позволяет выяснить, правильно ли веб-служба открыла запись; но обратная связь показывает, что веб-служба считает свои действия правильными, так как возвращается значение "истина" (см. рис. 3.19). Нам желательно увидеть данные, извлекаемые из источника данных, поэтому тестовая структура технологии .NET не подходит для модульного тестирования рассматриваемой службы.

    (рис 3.19) Ответ метода Open в браузере

    Создание тестовой структуры

    Текущее значение, открытое в методе Open, сохраняется в свойстве Event класса Events. Класс Events предназначен для вызова методов Open или Add и возврата значения, определяющего успех или неудачу вызова. В случае удачного выполнения вызова из свойства Event можно получить данные Event.

    Тестовая структура, предоставляемая технологией .NET, не отображает значения в свойстве веб-службы, и свойство нельзя определить как веб-метод с помощью идентификатора [ WebMethod ]. Идентификатор WebMethod располагается над всеми общими методами, представляемыми в качестве веб-служб XML. В данном случае нужна тестовая структура, использующая веб-службу и отображающая результаты таким образом, чтобы показать правильное функционирование кода.

    Прекрасным механизмом для реализации тестовой структуры веб-службы являются веб-формы. Для метода Add класса Events была создана форма EventClient.aspx, реализующая добавление события. В веб-форму был добавлен элемент управления датой для фиксирования даты от конечного пользователя, и два текстовых поля для имени и описания события. Для отображения результирующего ID нового события после успешного выполнения функции Add в форму добавлено текстовое поле, отображающее ID, и кнопка Submit (Отправить), после нажатия на которую веб-форма выдает ответ. На рисунке 3.20 показан метод Add элемента управления датой после добавления события.

    (рис 3.20) Тестовая структура веб-формы с методом Add

    Код веб-формы EventClient.aspx, приведенный в листинге 3.10, несложен. Как видно из рисунка 3.20, для каждого свойства класса anEvent в форме присутствует элемент управления. Кнопка Submit используется для инициализации вызова события Add. Код веб-формы EventClient.aspx содержит событие нажатия на кнопку элемента управления btnAdd. Веб-форма EventClient.aspx должна выполняться в том же веб-приложении, что и веб-служба. Ее назначением является только модульное тестирование кода, поэтому форма максимально упрощена; она реально обеспечивает разработчика обратной связью, информирующей о производительности веб-службы Events.

    /// <summary>
    /// Summary description for EventClient.
    /// </summary>
    public class EventClient : System.Web.UI.Page
    {
         protected System.Web.UI.WebControls.Calendar Calendar1;
         protected System.Web.UI.WebControls.TextBox txtName;
         protected System.Web.UI.WebControls.Label Label1;
         protected System.Web.UI.WebControls.Label Label2;
         protected System.Web.UI.WebControls.TextBox txtDescription;
         protected System.Web.UI.WebControls.Label lblFeedback;
         protected System.Web.UI.WebControls.Button btnAdd;
    
         private void Page_Load(object sender, System.EventArgs e)
         {
              //don't show the feedback text box until
              //there is a good reason
              this.lblFeedback.Visible = false;
    
              //make the numbers red that are selected in calendar
              this.Calendar1.SelectedDayStyle.ForeColor =
                   System.Drawing.Color.Red;
         }
    
         #region Web Form Designer generated code
         override protected void OnInit(EventArgs e)
         {
         //
         // CODEGEN: This call is required by the 
         // ASP.NET Web Form Designer.
         //
           InitializeComponent();
              base.OnInit(e);
         }
         
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
     {   
       this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
       this.Load += new System.EventHandler(this.Page_Load);
    
     }
         #endregion
    
         private void btnAdd_Click(object sender, System.EventArgs e)
         {
              Events myEvents = new Events();
              anEvent myEvent = new anEvent();
    
              //get the name
              myEvent.Name = this.txtName.Text;
    
              //get the date/time
              myEvent.DateTime = this.Calendar1.
                   SelectedDate.Date;
    
              //get the description
              myEvent.Description = this.txtDescription.Text;
              
              myEvents.Event = myEvent;
    
              //add it
              if (myEvents.Add())
              {
                   this.lblFeedback.Visible = true;
                   lblFeedback.Text = "Wrote a new date with ID = " +
                        myEvent.ID;
                   
              }
              else
              {
                   this.lblFeedback.Visible = true;
                   lblFeedback.Text = "Unable to write date";
              }
         }
    }

    Дизайнер компонентов автоматически добавил событие Click при двойном щелчке мышью на элементе управления btnAdd. В начале события Click созданы экземпляры класса anEvent и веб-службы Events. Так как эти классы находятся в едином пространстве имен, не нужно обращаться к пространству имен или размещать директиву using вверху исходного кода для пространства имен myPortal. Добавляемое событие представляется экземпляром anEvent с именем myEvent. Экземпляр класса Events называется myEvents. Свойства myEvent извлекаются из календарных элементов управления txtDescription и txtName. При заполнении пользователем экземпляра myEvent данными оно располагается в свойстве event экземпляра myEvents. Вызывается функция Add класса myEvents, и при ее успешной работе в элемент управления lblFeedback записывается сообщение об успехе функции; в противном случае – сообщение об ошибке.

    Следует заметить, что в данном коде не предусмотрена обработка ошибок, поскольку он предназначен для тестирования и не используется при функционировании веб-службы. Отсутствие системы обработки ошибок позволит получить больше данных для проверки кода на устойчивость к ошибкам, возникающим в процессе работы.

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

    Страницы:

    ASP.NET предлагает разработчику два основных типа проектов веб-приложения: веб-формы и веб-службы. Веб-формы обеспечивают логику представления при использовании бизнес-логики или логики данных. Веб-службы обеспечивают бизнес-логику в интернете и использование логики данных. Проект веб-службы не содержит элементы управления логики представления. Большинство веб-служб создаются для использования другими веб-службами, программами на альтернативных платформах или веб-формой перед получением пользователем данных, обработанных веб-службой.

    Архитектура веб-служб

    Служба ASP.NET состоит из библиотеки DLL, файла ASMX и файла Codebehind, который содержит класс, специально созданный для работы с веб-службами. Файл ASMX аналогичен файлу ASPX веб-формы ASP.NET, через него выполняются запросы веб-служб. В данной лекции мы будем считать веб-службу и класс с интегрированным HTTP-интерфейсом равнозначными. В проект можно добавлять другие классы, при этом не нужно создавать другой файл ASMX, так как классы сами по себе не являются веб-службами. При добавлении класса создается только файл для поддержки кода класса с именем <имя_класса>.cs или <имя класса>.<расширение_языка>.

    В проект веб-службы Visual Studio .NET может входить несколько веб-служб. При компиляции проекта результат записывается в одну библиотеку DLL. Она включает классы нескольких файлов ASMX и дополнений к классам, поддерживающим файлы веб-службы ASMX.

    Файл ASMX связан в IIS с расширением ISAPI aspnet_isapi.dll, поэтому при запросе файла ASMX из IIS aspnet_isapi.dll направляет HTTP-запрос нужной DLL-библиотеке веб-службы (см. рис. 3.1). Файл ASMX связан в один момент времени с одной DLL-библиотекой. Как правило, проекты веб-служб Visual Studio .NET располагают DLL в каталоге двоичных файлов внутри папки с файлами ASMX.

    Веб-службы ASP.NET используют те же файлы конфигурации библиотеки и файлы конфигурации реализации IIS, что и веб-формы ASP.NET. Файлы веб-службы ASP.NET, используемые компилятором для создания веб-службы в библиотеке, именуются <имя_веб-службы>.asmx (в отличие от файлов веб-форм ASP.NET с расширением .aspx ). Веб-служба ASP.NET имеет ту же самую архитектуру, равно как и типы файлов, связанные с ней внутри проекта. Например, файл ASMX является адресуемой входной точкой веб-службы и как главный файл предоставляет директивы обработки для компиляции веб-службы. Файл ASMX описывает файлы, предназначенные для веб-службы ASP.NET, и сам может содержать в себе код. Код, связанный с веб-службой ASP.NET, обычно располагается в файле Codebehind. Файл Codebehind <моя_служба>.asmx.cs содержит исходный код веб-службы, а файл <моя_служба>.asmx.resx является источником, обслуживающим файл Codebehind.

    (рис 3.1) Обзор архитектуры веб-служб

    Примечание. Файл ASMX может включать код вместо файла Codebehind, однако Visual Studio .NET использует файл Codebehind в шаблоне проекта веб-службы по умолчанию. Если файл ASMX не использует Codebehind, то технология .NET компилирует файл и создает DLL автоматически по первому запросу.

    Создание веб-службы с помощью Visual Studio .NET

    Для демонстрации процесса создания веб-службы в данной лекции представлено простое приложение для составления расписания по датам. Рассматриваемая система расписания состоит из трех следующих классов.

  • anEvent. Класс представляет событие, происходящее в данный момент времени и день.
  • Events. Класс представляет коллективную группу экземпляров класса anEvent.
  • EventClient. Веб-форма, использующая экземпляры Events и anEvent.
  • Класс Events является веб-службой, класс anEvent – классом, используемым веб-службой Events, а класс EventClient – веб-формой, использующей классы anEvent и Events. Все классы располагаются в едином пространстве имен myPortal. В результате компиляции проекта создается библиотека myPortal.dll. Файл Events.asmx указывает на класс в библиотеке myPortal.dll. Библиотека myPortal.dll также содержит класс веб-формы EventClient и класс anEvent. Файл EventClient.aspx направляет технологию .NET в myPortal.dll как в скомпилированный код, содержащий соответствующий класс.

    Создадим новый проект веб-службы ASP.NET.

  • Откройте Visual Studio .NET. Отобразится начальная страница (если в Visual Studio .NET не отключено отображение начального окна). Как и в предыдущих версиях Visual Basic, предоставляется окно с шаблоном по умолчанию (вы можете его отключить).
  • Выберите File\New Project (Файл\Создать проект) для открытия окна со списком типов проектов и соответствующих шаблонов.
  • В списке слева выберите узел Visual C# Projects (Проекты Visual C#) – справа отобразятся шаблоны проектов для языка C#.
  • Выберите шаблон ASP.NET Web Service (Веб-служба ASP.NET).
  • Новой веб-службе в любом проекте по умолчанию присваивается имя Service1. В нашем примере проект называется myPortal (см. рис. 3.2). Задайте путь к корневому веб-каталогу сервера при помощи адреса URL или UNC. В URL указывается имя сервера по умолчанию localhost, если разрабатываемое программное решение располагается на рабочей станции при написании кода и модульном тестировании.
  • Нажмите на кнопку OK.
  • Visual Studio .NET выполнит подключение к веб-серверу, указанному в диалоговом окне New Project (Новый проект), создаст новый виртуальный каталог и скопирует в него файлы веб-служб по умолчанию.

    После генерации файлов проекта отобразится представление Design (Дизайн) веб-службы Service1, готовое к разработке. Visual Studio .NET присваивает веб-файлам веб-службы имена по умолчанию, поэтому их нужно переименовать.

  • Откройте Solution Explorer в меню View\Solution Explorer (Вид\Диспетчер программы).
  • Щелкните правой кнопкой мыши на файле Service1.asmx и выберите Rename (Переименовать).
  • Удалите имя Service1 и введите имя Events.
  • Нажмите на клавишу Enter, и имена файлов ASMX, Codebehind и файла разрешения сменятся на Events.asmx, Events.asmx.cs и Events.asmx.res соответственно.
  • Класс, обеспечивающий работу веб-службы, будет по-прежнему называться Service1. Веб-служба исправно функционирует с различными именами, но мы все-таки сменим имя класса, присвоив ему имя службы.

  • Выделите файл Events.asmx в диспетчере Solution Explorer и нажмите на кнопку View Designer (Отобразить дизайнер). Файл Events.asmx.cs отобразится в дизайнере компонентов).
  • Щелкните правой кнопкой мыши на файле Events.asmx.cs и выберите Properties (Свойства).
  • В открывшемся окне свойство Name (Имя) содержит значение Service1. Удалите это значение.
  • Введите в свойстве имени значение Events. Дизайнер компонентов автоматически изменит каждый экземпляр имени класса в файле Codebehind.
  • Веб-служба Events выполняет добавление, открытие или удаление экземпляров класса anEvent из портала. Данные экземпляра anEvent сохраняются в базе данных.

    (рис 3.2) Новый проект myPortal, создаваемый в диалоговом окне New Project (Новый проект)

    Класс Events взаимодействует с базой данных, поэтому при построении веб-службы мы используем компоненты инструментария, реализующие получение и отправку (см. рис. 3.3). Компоненты добавятся в представление Design (Дизайн) веб-службы Events.

    (рис 3.3) Компоненты дизайнера форм, добавленные в представлении Design (Дизайн) веб-службы Events

    Работа с дизайнером компонентов

    Инструментарий предоставляет множество компонентов для добавления в веб-службу. Некоторые из них доступны как в веб-формах, так и в веб-службах, однако в проекте веб-службы недоступны компоненты, предназначенные для создания графического интерфейса для пользователя. Добавление компонентов веб-службы в представлении Design (Дизайн) не влияет на файл Events.asmx. ASMX аналогичен файлу веб-формы ASPX, так как связывает язык, класс, файл Codebehind и файл источника с веб-службой. Отличие заключается в том, что ASMX не содержит другой информации, предназначенной для отображения.

    Содержимое файла Events.asmx приведено в листинге 3.1. Если бы этот код предназначался для веб-формы, он содержал бы больший объем кода XML и HTML, определяющего параметры отображения; но он предназначен для веб-службы и содержит лишь одну строку текста.

    <%@ WebService Language="c#" Codebehind="Events.asmx.cs" 
    Class="myPortal.Events" %>

    При добавлении компонента подключения (или любого другого) в представление Design (Дизайн) в окне свойств настраиваются параметры этого компонента. Для каждого значения, устанавливаемого в этом окне, дизайнер компонентов генерирует код инициализации в файле Codebehind веб-службы и определяет предпроцессорную команду. Предпроцессорные команды в C# аналогичны командам в C++ и C, отличие же заключается в следующем.

  • Предпроцессорные команды языка C# не интерпретируются предпроцессором, отдельным от компилятора. Компилятор и предпроцессор представляют единое целое.
  • Команды предпроцессора C# не могут указывать макросы.
  • Команда #region указывает область, расширяемую или сужаемую в редакторе Visual Studio .NET. Команда #region не оказывает функционального влияния на программное решение.

    В коде, сгенерированном дизайнером компонентов, Visual Studio. NET объявляет три элемента:

  • ссылку на Icontainer с именем components ;
  • подпрограмму InitializeComponent ;
  • функцию Dispose.
  • Все компоненты, добавленные в представлении Design (Дизайн), инициализируются в подпрограмме InitializeComponent. Вызов подпрограммы InitializeComponent автоматически размещается в конструкторе веб-службы. Все свойства, устанавливаемые в окне свойств (см. рис. 3.3), присваиваются компоненту, которому они принадлежат. Комментарии, размещаемые дизайнером компонентов в данной области, предупреждают о том, что не следует изменять код вручную. Область кода, сгенерированного дизайнером компонентов, отображается в виде секции, взаимодействующей с представлением Design (Дизайн) веб-службы.

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

    Настройка компонента подключения к базе данных

    Источником данных для веб-службы Events является база данных SQL Server 2000, поэтому добавим компонент SQLConnection. При поддержке другого типа базы данных используется компонент OleDBConnection. Компонент SQLConnection дает дополнительные возможности по управлению и повышает эффективности работы, в отличие от компонента OleDBConnection, поэтому последний используется только в случае необходимости. Если для источника данных отсутствует провайдер OLE-DB, то загрузите с сайта Microsoft доступен провайдер ODBC, поставляемый в отдельной библиотеке.

    Свойство ConnectionString в окне свойств позволяет выбрать предыдущие подключения, настроенные на рабочей станции, или создать новое подключение. Для нашего примера создадим новое подключение (см. рис. 3.4).

    (рис 3.4) Выбор нового подключения в свойстве ConnectionString компонента SQLConnection

    После выбора <New Connection...> в качестве параметра свойства ConnectionString откроется окно Data Link Properties (Свойства подключения к данным). В этом окне настраиваются все параметры, представленные в строке подключения ADO.NET. Окно свойств подключения к данным – это общее окно Visual Studio для настройки подключения ADO. Для веб-службы Events используется сервер базы данных, расположенный на сервере AMD1700, и база данных ASPNETServices (см. рис. 3.5).

    (рис 3.5) Настройка нового подключения к данным для веб-службы Events

    Изменим имя компонента по умолчанию SQLConnection1 на более дружественное пользователю ServicesDBConn. После настройки и создания подключение SQLConnection работает в любом месте веб-службы. Класс Events наследуется из System.Web.Services. Класс WebService содержит множество классов и функций для взаимодействия с HTTP-соединением. В листинге 3.2 приведен исходный код, сгенерированный Visual Studio .NET для класса Events.

    /// <summary>
    /// Summary description for Events.
    /// </summary>
    public class Events : System.Web.Services.WebService
    {
         public Events()
         {
              //CODEGEN: This call is required by the 
              //ASP.NET Web Services Designer
              InitializeComponent();
         }
    
         private System.Data.SqlClient.SqlConnection ServicesDBConn;
    
         #region Component Designer generated code
         
         //Required by the Web Services Designer
         private IContainer components = null;
                   
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
         {
              this.ServicesDBConn = new 
                                 System.Data.SqlClient.SqlConnection();
              //
              // ServicesDBConn
              //
              this.ServicesDBConn.ConnectionString = 
              "data source=amd1700;initial catalog=ASPNETServices;" +
              "integrated security=SSPI;persist security info=False;" +
              "workstation id=AMD1700;packet size=4096";
    
         }
    
         /// <summary>
         /// Clean up any resources being used.
         /// </summary>
         protected override void Dispose( bool disposing )
         {
              if(disposing  components != null)
              {
                   components.Dispose();
              }
              base.Dispose(disposing);
         }
         
         #endregion
    }

    При работе с элементами управления веб-службы использование дизайнера компонентов не обязательно. Элементы управления добавляются в веб-службу посредством объявления и конструирования в файле Codebehind, как и другие переменные и классы. Например, экземпляр объекта SQLConnection можно создать и инициализировать в конструкторе класса Events. Код, приведенный в листинге 3.3, демонстрирует создание локального экземпляра объекта SQLConnection без использования дизайнера компонентов и компонента SQLConnection.

    public class Events : System.Web.Services.WebService
    {
         //locals to class
         private System.Data.SqlClient.SqlConnection myConn;
    
         public Events()
         {
              //CODEGEN: This call is required by the 
              //ASP.NET Web Services Designer
              InitializeComponent();
    
              System.Configuration.AppSettingsReader
                   myAppSettings =
                   new System.Configuration.AppSettingsReader();
    
              //get the connection string from web.config
              string sConnect =
                   ((string)
                   (myAppSettings.GetValue
                   ("ProductionDB.ConnectionString",
                   typeof(string))));
    
              //make the DB connection
            myConn = new System.Data.SqlClient.SqlConnection(sConnect);
         }
    
         #region Component Designer generated code
         
         //Required by the Web Services Designer
         private IContainer components = null;
                   
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
         {
         }
         /// <summary>
         /// Clean up any resources being used.
         /// </summary>
         protected override void Dispose( bool disposing )
         {
              if(disposing  components != null)
              {
                   components.Dispose();
              }
              base.Dispose(disposing); 
         }
         
         #endregion
    }

    В листинге 3.3 экземпляр myConn объявляется локально по отношению к классу Events. Строка подключения извлекается из файла web.config и используется в качестве конструктора экземпляра myConn. После создания экземпляра myConn в конструкторе Events его можно использовать в любом месте класса.

    Считывание параметров приложения из web.config

    Файл web.config великолепно подходит для получения инициализационных данных, индивидуальных для веб-приложения. Это позволяет создавать решение программно, чтобы получить аргументы, связанных с конкретной реализацией, без внесения изменений в код других реализаций. С помощью класса AppSettingsReader осуществляется считывание данных из секции <appSettings> файла web.config. В листинге 3.4 приведен файл web.config, содержащий строку подключения к базе данных, полученную в листинге 3.3 в конструкторе Events.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>   
      <system.web>
        <compilation defaultLanguage="c#" debug="true"/>
        <customErrors mode="RemoteOnly" />
    </system.web>
    
     <appSettings>
        <add key="ProductionDB.ConnectionString"
              value="data source=amd1700;
                     initial catalog=ASPNETServices;
                     integrated security=SSPI;
                     persist security info=False;
                     workstation id=AMD1700;
                     packet size=4096" 
         />
      </appSettings>
    
    </configuration>

    Дизайнер компонентов содержит механизм, использующий элемент appSettings файла web.config. В окне свойств компонента подключения в представлении Design (Дизайн) рассматриваемой веб-службы можно настроить параметр ConnectionString как набор динамических свойств DynamicProperties (см. рис.3.6).

    (рис 3.6) Свойства компонента подключения, использующие DynamicProperties для получения строки подключения ConnectionString

    По умолчанию свойство ConnectionString не настроено на использование DynamicProperties. Каждое свойство нужно связать с ключом в файле web.config. Ключ представляет собой атрибут с именем key, расположенный в элементе add, подчиненном элементу appSettings (см. рис. 3.4). При нажатии на кнопку "..." в свойстве ConnectionString откроется диалоговое окно для выбора ключа (см. рис. 3.7).

    (рис 3.7) Связывание свойства ConnectionString с ключом в файле конфигурации

    Компонент Data Adaptor

    После установки подключения веб-служба узнает источник данных и аутентификационные данные, необходимые для предоставления этому источнику. Далее веб-служба отправляет запрос источнику данных на получение данных. ADO.NET предлагает для этого еще один компонент – адаптер данных (data adaptor). Версия адаптера данных для SQL Server 2000 использует провайдер SQL Server .NET с именем SQLDataAdaptor. OledbDataAdaptor (версия для OLE-DB) работает с предыдущими версиями SQL Server или базами данных, не имеющих конкретного провайдера. Адаптер данных можно установить, настроить и применить, как и любой другой класс в ADO.NET. ASP.NET содержит в инструментарии альтернативный компонент, который можно использовать в случае необходимости. Адаптер данных добавляется в дизайнере компонентов веб-службы и настраивается в окне свойств.

    При добавлении компонента открывается окно мастера настройки адаптера данных (Data Adaptor Configuration Wizard). Это окно при желании можно закрыть, а свойства настроить вручную.

    Ниже приведены инструкции по работе с мастером настройки адаптера данных.

  • В стартовом окне нажмите на кнопку Next (Далее), чтобы начать настройку адаптера данных, добавленного в веб-службу.
  • В окне Choose Your Data Connection (Выбор подключения к данным) (см. рис. 3.8) в ниспадающем списке выберите подключение к данным, настроенное для проекта. При нажатии на кнопку New Connection (Создать подключение) откроется окно Data Link Properties (Свойства подключения к данным) (см. рис. 3.5) для настройки соединения. В нашем примере выберите ранее созданное подключение.(рис 3.8) Мастер настройки адаптера данных: выбор подключения к данным
  • После выбора подключения нажмите на кнопку Next (Далее), и мастер предложит выбрать метод запроса данных из источника в окне Choose A Query Type (Выбор тип запроса) (см. рис. 3.9).

    (рис 3.9) Мастер настройки адаптера данных: выбор типа запроса

    Выберите Use SQL Statements (Использовать операторы SQL), и мастер сгенерирует набор командных объектов (с помощью класса SQLCommand или OledbCommand ) в функции InitializeComponent класса веб-службы. Командные объекты будут созданы с набором SQL-команд, которые используют псевдонимы каждого имени поля. С помощью ссылки на псевдоним в экземпляре командного объекта можно установить значения рассматриваемого поля.

    Выберите Create New Stored Procedures (Создать новые сохраненные процедуры) или Use Existing Stored Procedures (Использовать имеющиеся сохраненные процедуры), и мастер запросит дополнительную информацию о таблицах данных, необходимую для построения и связи сохраненных процедур.

  • В нашем примере выберите опцию Use SQL Statements.
  • Нажмите на кнопку Next (Далее) для перехода в окно Generate The SQL Statements (Генерация выражений SQL). Введите команду Select SQL для набора данных, используемых адаптером при построении выражений SQL (см. рис. 3.10). SQLDataAdaptor и OledbDataAdaptor поддерживают интерфейс для выполнения операторов Select (Выбор), Insert (Вставка), Delete (Удаление) и Update (Обновление). Мастер генерирует команды SQL для управления данными, устанавливаемыми в адаптере данных. Он запрашивает выражение SQL Select для создания текста команд Select, Insert, Delete и (рис 3.10) Мастер настройки адаптера данных: генерация выражений SQL
  • После ввода в поле оператора Select теста select * from tblEvents нажмите на кнопку Next (Далее), чтобы отобразить окно подтверждения View Wizard Results (Просмотр результатов работы мастера) (см. рис. 3.11). В этом окне описываются действия, выполненные с кодом веб-службы. При возникновении проблем с анализом выражения SQL, введенного в предыдущем шаге, в этом окне вы не увидите все возможные комментарии SQL, показанные на рис. 3.11. Нажмите на кнопку Back (Назад) и повторите введенную команду SQL или выберите создание сохраненных процедур на сервере базы данных.
  • (рис 3.11) Мастер настройки адаптера данных: просмотр результатов работы

    В нашем примере адаптер данных называется SQLDataAdaptor1, так как дизайнер компонентов присваивает это имя по умолчанию любому компоненту SQLDataAdaptor, добавляемому в представлении Design (Дизайн). Это имя можно изменить в любой момент, и сгенерированный мастером код в подпрограмме InitializeComponent веб-службы автоматически обновится. В нашем примере имя SQLDataAdaptor изменено на EventsAdaptor в окне свойств.

    Объектам SQLCommand, сгенерированным мастером настройки адаптера данных, присвоены имена SQLSelectCommand1, SQLDeleteCommand1, SQLInsertCommand1 и SQLUpdateCommand1. Имена командных объектов заменены на SelectEvent, DeleteEvent, InsertEvent и UpdateEvent соответственно. Это было сделано в окне свойств SQLDataAdaptor, т.е. изменено свойство имени, подчиненное динамическим свойствам Dynamic Properties каждого командного объекта. В листинге 3.5 приведен код, сгенерированный дизайнером компонентов для адаптера данных EventsAdaptor и объекта подключения к данным ServicesDBConn.

    private void InitializeComponent()
    {
         System.Configuration.AppSettingsReader 
         configurationAppSettings = new 
         System.Configuration.AppSettingsReader();
         
       this.ServicesDBConn = new System.Data.SqlClient.SqlConnection();
    
       this.EventsAdaptor = new System.Data.SqlClient.SqlDataAdapter();
    
       this.SelectEvent = new System.Data.SqlClient.SqlCommand();
    
       this.InsertEvent = new System.Data.SqlClient.SqlCommand();
    
       this.UpdateEvent = new System.Data.SqlClient.SqlCommand();
    
       this.DeleteEvent = new System.Data.SqlClient.SqlCommand();
    
         //
         // ServicesDBConn
         //
          this.ServicesDBConn.ConnectionString = 
                            ((string)(configurationAppSettings.GetValue
                 ("ServicesDBConn.ConnectionString", typeof(string))));
    
         //
         // EventsAdaptor
         //
         this.EventsAdaptor.DeleteCommand = this.DeleteEvent;
    
         this.EventsAdaptor.InsertCommand = this.InsertEvent;
    
         this.EventsAdaptor.SelectCommand = this.SelectEvent;

    В листинге 3.5 приведена часть подпрограммы InitializeComponent, демонстрирующая компоненты SQLCommand, созданные дизайнером компонентов после переименования. После создания класса AppSettingsReader для получения параметров приложения из файла web.config созданы классы подключения, адаптера данных и команд. После инициализации класса подключения посредством строки подключения, полученной из файла web.config, класс адаптера данных присваивает каждый экземпляр объекта SQLCommand соответствующему свойству команды. Например, экземпляр команды DeleteEvent присваивается свойству DeleteCommand экземпляра SQLDataAdaptor с именем EventsAdaptor.

    Дизайнер компонентов сгенерировал достаточно большой объем кода для настройки текста команд SQL и параметров каждого экземпляра командных объектов (это не отражено в листинге 3.5). Он определил каждое поле в наборе данных посредством псевдонима. Псевдонимы в командном объекте были установлены в качестве параметров. В листинге 3.6 приведен исходный код, реализующий текст команды SQL и параметры командного объекта InsertEvent.

    //
    // InsertEvent
    //
    this.InsertEvent.CommandText = "INSERT INTO tblEvent" + 
               "(Name, StartDate, Description) " + 
              "VALUES (@Name, @StartDate, @Description); "+ 
              "SELECT Name, StartDate, Description, ID " + 
              "FROM tblEvent WHERE (ID = @@IDENTITY)";
    
    this.InsertEvent.Connection = this.ServicesDBConn;
    
    this.InsertEvent.Parameters.Add(
                                new System.Data.SqlClient.SqlParameter
                 ("@Name", System.Data.SqlDbType.VarChar, 50, "Name"));
    
    this.InsertEvent.Parameters.Add(
                                new System.Data.SqlClient.SqlParameter
                        ("@StartDate", System.Data.SqlDbType.DateTime, 
                           8, "StartDate"));
    
    this.InsertEvent.Parameters.Add(
                                 new System.Data.SqlClient.SqlParameter
                        ("@Description", System.Data.SqlDbType.VarChar, 
                                                  200, "Description"));

    Класс Data Reader

    С учетом псевдонимов, сгенерированных дизайнером компонентов, можно разработать код, устанавливающий параметры и заполняющий объект DataReader. Например, функция Add веб-службы Events использует экземпляр класса команды InsertEvent для добавления нового события. Псевдонимы имен полей таблицы базы данных, определенные при инициализации класса InsertEvent, используются методом Parameters для присвоения значения полю, представляемому псевдонимом в команде Insert SQL, используемой классом InsertEvent для обновления таблицы базы данных. В листинге 3.7 приведен код функции Add.

    /// <summary>
    /// Adds a new event to system
    /// </summary>
    [WebMethod]
    public bool Add()
    {
         System.Data.SqlClient.SqlDataReader myEvent;
    
     try
     {
      //set state for the command object instance
      InsertEvent.Parameters["@Name"].Value =mEvent.Name;
    
      InsertEvent.Parameters["@StartDate"].Value =mEvent.DateTime;
    
      InsertEvent.Parameters["@Description"].Value =mEvent.Description;
    
       //open the connection object
      ServicesDBConn.Open();
    
      //run query
      myEvent = this.InsertEvent.ExecuteReader();
       
      //must advance to first record
      myEvent.Read();
      
      //fill local properties
      mEvent.ID =  myEvent.GetInt32(3);
    
      //shut down record
      myEvent.Close();
     }
     catch(System.Exception Err )
     {
      //log error
      this.LogMessage(Err.ToString() , true);
      
      //rethrow for client consumption
      throw Err;
     }
    
      //success if we get this far
      return true;
    }

    Экземпляр класса SQLDataReader создан для доступа к данным, возвращаемым из экземпляра класса SQLCommand с именем InsertEvent. Экземпляр InsertEvent установлен в качестве свойства объекта SQLDataAdaptor EventsAdaptor и имеет объект подключения servicesDBConn, поэтому он возвращает данные экземпляру объекта SQLDataReader с именем myEvent. Локальный экземпляр anEvent с именем mEvent используется для хранения текущего состояния события веб-службы Events. В листинге 3.8 приведен исходный код класса anEvent.

    /// <summary>
    /// a single event
    /// </summary>
    public class anEvent
    {
    
         private long CurrentID;
         private string CurrentName;
         private string CurrentDescription;
         private DateTime CurrentDateTime;
    
         public anEvent()
         {
         }
    
         /// <summary>
         /// ID for opened event
         /// </summary>
         public long ID
         {
              get
              {
                   return CurrentID;
              }
              set
              {
                   CurrentID = value;
              }
         }
    
         /// <summary>
         /// Date and time of actual event
         /// </summary>
         public DateTime DateTime
         {
              get
              {
                   return CurrentDateTime;
              }
              set
              {
                   CurrentDateTime = value;
              }
         }
    
         /// <summary>
         /// description of event
         /// </summary>
         public string Description
         {
              get
              {
                   return CurrentDescription;
              }
              set
              {
                   CurrentDescription = value;
              }
         }
    
         /// <summary>
         /// Name for opened event
         /// </summary>
         public string Name
         {
              get
              {
                   return CurrentName;
              }
              set
              {
                   CurrentName = value;
              }
         }
    }

    Как видно из листинга 3.8, класс anEvent содержит данные, формирующие событие Event. Веб-служба Events считывает и записывает значения события в источник базы данных, поэтому для класса Events объявлен и создан локальный экземпляр anEvent.

    Когда функция Add (см. листинг 3.7) устанавливает параметры для Name, Date и Description, она берет соответствующие значения свойств для экземпляра mEvent класса anEvents. Свойства mEvent устанавливаются в веб-службе Events потребителем или извлекаются из базы данных для Event, загружаемой в веб-службу. После установки параметров команды InsertEvent соединение открывается, команда выполняется и происходит считывание результатов в экземпляре myEvent с помощью команды ExecuteReader объекта SQLCommand InsertEvent. Команда Read объекта SQLDataReader выполняет переход к следующей строке набора данных, представляющей первую строку во вновь открытом наборе данных. Если считывание myEvent происходит без перехода к первой строке, в ADO.NET возникает ошибка. Локальный экземпляр anEvent обновляется новым ID, так как база данных генерирует ID для конкретного класса anEvent.

    Многие программисты, знакомые с версиями ADO, предшествовавшими технологии ADO.NET, удивятся тому, как используется порядковая позиция поля ID, на которую ссылаются для получения значения ID экземпляра mEvent. Порядковое значение любого поля не должно использоваться для обращения к значению в наборе записей. SQLDataReader похож на набор записей ADO. Если таблица базы данных обновляется с добавлением нового столбца, то набор записей ADO может записать неправильное значение поля. Это зависит от того, в каком месте таблицы добавлен новый столбец. Однако в технологии ADO.NET этой проблемы не существует, так как порядковая позиция определяется в терминах экземпляра адаптера данных. Адаптер данных обеспечивает определенный уровень абстрагирования от уровня данных, поэтому программист может обращаться к порядковой позиции, не опасаясь, что изменения в таблице базы данных повредят программное обеспечение.

    Компонент Event Log

    Весь код взаимодействия с базой данных в листинге 3.7 расположен в секции Try блока Try .. Catch. Если удовлетворяется условие исключения, то выполняется блок Catch. Экземпляр класса System.Exception передается в качестве аргумента функции Catch и содержит информацию об ошибке, возникшей в блоке Try. В листинге 3.7 экземпляр класса System.Exception упорядочен в строку с помощью функции ToString. Функция ToString представляет собой общую функцию, используемую со многими классами, так как она унаследована из класса object. Строковое представление сообщения об ошибке передается функции LogMessage, которая записывает ошибку в журнал Application Log в программе Event Viewer (Просмотр событий), после чего ошибка передается приложению-потребителю.

    Функция LogMessage реализована таким образом, что в журнал Application несущего сервера можно записать любую информацию. Журнал Application поддерживает способ идентификации типа записываемого сообщения. Функция LogMessage записывает сообщения типа error или information в зависимости от переданного значения параметра Error. Если значение параметра Error равняется "истине", LogMessage идентифицирует сообщение в журнале Application как error (ошибка); в противном случае сообщение определяется как information (сведения). В листинге 3.9 приведен код функции LogMessage.

    /// <summary>
    /// logs messages to event log
    /// in: string that contains message of error
    ///          or information that ought to be logged
    ///          
    ///     out: returns true if successful write,
    ///          rethrows the error if failure occurs
    /// </summary>
    ///
    [WebMethod]
    public bool LogMessage(string Message, bool Error)
    {
         System.Diagnostics.EventLogEntryType MessageType;
    
         try
         {
              //determine the type of message
              if (Error )
              {
                   MessageType =
                        System.Diagnostics.EventLogEntryType.Error;
              }
              else
              {
                   MessageType =
                   System.Diagnostics.EventLogEntryType.Information;
              }                    
              
              //make the write
              this.Log.WriteEntry(Message, MessageType);
         }
         catch (System.Exception eLogWrite)
         {
              //nothing else left to do except throw the raw error
              throw eLogWrite;
         }               
         //we have success if we get to this line
         return true;
    }

    Экземпляр объекта Log является журналом приложения несущего сервера. Дизайнер компонентов содержит компонент, представляющий журнал несущего сервера в программе Event Viewer (Просмотр событий). Просто вставьте элемент управления в представлении Design (Дизайн) веб-службы, после чего настройте свойства журнала на том узле, куда будут передаваться сообщения. В данном случае подходит журнал Application (Приложение), так как он фиксирует информацию о приложении.

    Фиксирование данных о состоянии и ошибок в журнале

    Во всех веб-службах на локальном сервере должны фиксироваться данные о состоянии и ошибках. Довольно часто в веб-проектах не предусматривается адекватная система обработки ошибок или предоставление диагностической информации о том, какие действия выполняются в коде. Если в приложении возникают ошибки, а ошибочное условие нельзя продублировать в среде разработки, то выход из положения – разрешить разработчику доступ к среде разработки для диагностирования и отладки кода. Приложение не должно диагностироваться в среде разработки. Поскольку разработчики могут просматривать сообщения, записанные в журнал приложения, и использовать простые методы записи информации в журнал Application, нет причин для выполнения подобных действий при диагностике ошибки. Разрешение доступа к среде разработки является большой ошибкой приложения, и ее легко избежать, обеспечив надежную систему обработки ошибок и ведение журналов диагностики.

    Безопасность приложения

    Запись данных в журнал событий не требует модификации прав, под которыми работает веб-служба. Новыми возможностями IIS6 являются две встроенные учетные записи для рабочих процессов: Network Service (Сетевая служба) и Local Service (Локальная служба). В предыдущих версиях рабочие процессы для IIS выполнялись под учетной записью Local System (Локальная система). Учетная запись Network Service (Сетевая служба) является учетной записью по умолчанию для выполнения рабочих процессов в IIS. Она более ограничительна по сравнению с Local System (Локальная система), а учетная запись Local Service (Локальная службы) – более ограничительна по сравнению с Network Service (Сетевая служба). Можно создать особую учетную запись для обеспечения определенных требований к безопасности приложения.

    Для настройки приложения на работу под другой учетной записью следует создать новый пул приложения и настроить его на работу под аутентификационными данными учетной записи Local System (Локальная система).

  • Откройте консоль MMC Computer Management (Управление компьютером) с помощью команды Start\Administrative Tools (Пуск\Администрирование).
  • Откройте узел Internet Information Services (IIS) Manager (Диспетчер IIS), затем – узел папки Aplication Pools (Пулы приложения). В дереве, расположенном под папкой Application Pools (Пулы приложений), в виде значков отобразятся все пулы приложения. В списке должен присутствовать пул приложения DefaultAppPool. При его развертывании отобразятся веб-сайты, использующие этот элемент (см. рис. 3.12).
  • Веб-сайт по умолчанию Default Web Site, созданный при установке IIS, использует пул DefaultAppPool. Как видно из рисунка 3.12, сайт Default Web Site присутствует в списке многих веб-приложений, использующих DefaultAppPool. Для настройки пула приложения откройте страницу свойств веб-сайта или виртуального каталога, щелкнув правой кнопкой мыши на узле и выбрав команду Properties (Свойства).(рис 3.12) Приложения, использующие DefaultAppPool, отображаются в консоли MMC Computer Management (Управление компьютером)
  • Откройте вкладку Home Directory (Домашний каталог) веб-сайта либо вкладку Virutal Directory (Виртуальный каталог) виртуального каталога. Во вкладке Home Directory (Домашний каталог) поле со списком Application Pool (Пул приложения) отображает доступные пулы приложения (см. рис. 3.13).

    (рис 3.13) Вкладка Home Directory (Домашний каталог) окна свойств веб-сайта по умолчанию

    Веб-служба Events работает в виртуальном каталоге myPortal, расположенном на сайте Default Web Site. Виртуальный каталог myPortal настроен на использование пула DefaultAppPool, работающего под аутентификационными данными учетной записи Network Service (Сетевая служба). Когда веб-служба Events попытается записать данные в журнал событий, произойдет ошибка, так как учетная запись Network Service (Сетевая служба) не имеет соответствующих прав. В предыдущей версии IIS запись в журнал приложений веб-службой Events осуществляется без всяких проблем. Для разрешения проблемы доступа создайте новый пул приложения, использующий учетную запись Local System, и настройте myPortal на работу с новым пулом приложения.

  • Щелкните правой кнопкой мыши на значке Application Pools (Пулы приложений) в консоли Computer Management (Управление компьютером) и выберите New\Aplication Pool (Создать\Пул приложения).
  • В диалоговом окне Add New Application Pool (Создание нового пула приложения) (см. рис. 3.14) видно, что идентификатор ID пула приложения равен значению webservice using LocalSystem. В нашем случае используются параметры по умолчанию нового пула приложения.(рис 3.14) Диалоговое окно Add New Application Pool (Создание нового пула приложения)
  • Нажмите на кнопку OK.
  • Ниже приведены шаги по смене объекта пула приложения webservice using LocalSystem.

  • Щелкнув правой кнопкой на вновь созданном узле webservice using LocalSystem в консоли MMC Computer Management (Управление компьютером) в узле Application Pools (Пулы приложения) и выберите пункт Properties (Свойства).
  • В окне свойств откройте вкладку Identity (Объект) и в списке учетных записей Predefined security account (Предопределенные учетные записи безопасности) выберите Local System (Локальная система) (см. рис. 3.15).(рис 3.15) Настройка учетной записи для пула приложения
  • Нажмите на кнопку OK или на кнопку Apply (Применить). Появится окно с предупреждением об опасностях, связанных с работой пула приложений в учетной записи Local System (Локальная система). Вы можете настроить учетную запись в соответствии с требованиями безопасности, чтобы веб-служба осуществляла запись в журнал событий приложения.
  • После создания пула приложения виртуальный каталог myPortal нужно настроить на использование пула приложения webservice using LocalSystem. Откройте окно свойств для myPortal и во вкладке Directory (Каталог) выберите новый пул приложения (см. рис. 3.16).

    (рис 3.16) Выбор нового пула приложения webservice using LocalSystem для myPortal

    Тестирование веб-службы

    Помимо метода Add (см. листинг 3.7) и функции LogMessage (см. листинг 3.9) служба Events содержит три общих веб-метода: Delete, Open и HelloWorld. Методы Delete и Open аналогичны функции Add, т.е. выполняют чтение и запись отправляемых пользователем данных. HelloWorld является методом по умолчанию, размещаемым дизайнером компонентов во всех новых веб-службах для демонстрации создания метода, доступного из интернета.

    Если файл ASMX веб-службы установлен в качестве стартового файла, веб-службу можно запускать и отлаживать в Visual Studio .NET. Нажмите на клавишу F5 для запуска компилятора; при отсутствии ошибок откроется браузер с отображением тестовой структуры технологии .NET (см. рис. 3.17).

    Щелкните на любом из вызовов метода для открытия новой страницы, отображающей форму записи данных на базе параметров метода. Для описываемого метода под полями записи данных определены и описаны запрос и ответ HTTP и SOAP.

    (рис 3.17) Запрос веб-службы с помощью браузера

    Данные, расположенные в текстовых полях на странице тестирования, будут переданы в качестве аргументов параметров метода после нажатия на кнопку Invoke (Запросить). На рисунке 3.18 показана страница тестирования метода Open в браузере.

    (рис 3.18) Метод Open веб-службы Events, запрошенный в браузере

    Присвойте ID значение 13 и нажмите на кнопку Invoke (Запросить), в результате чего откроется другое окно браузера с результатом вызова метода Open в формате XML. Метод Open возвращает значение "истина" или "ложь" в случае успеха или неудачи (соответственно) выполняемой функции, поэтому такой ответ неинформативен. Он не позволяет выяснить, правильно ли веб-служба открыла запись; но обратная связь показывает, что веб-служба считает свои действия правильными, так как возвращается значение "истина" (см. рис. 3.19). Нам желательно увидеть данные, извлекаемые из источника данных, поэтому тестовая структура технологии .NET не подходит для модульного тестирования рассматриваемой службы.

    (рис 3.19) Ответ метода Open в браузере

    Создание тестовой структуры

    Текущее значение, открытое в методе Open, сохраняется в свойстве Event класса Events. Класс Events предназначен для вызова методов Open или Add и возврата значения, определяющего успех или неудачу вызова. В случае удачного выполнения вызова из свойства Event можно получить данные Event.

    Тестовая структура, предоставляемая технологией .NET, не отображает значения в свойстве веб-службы, и свойство нельзя определить как веб-метод с помощью идентификатора [ WebMethod ]. Идентификатор WebMethod располагается над всеми общими методами, представляемыми в качестве веб-служб XML. В данном случае нужна тестовая структура, использующая веб-службу и отображающая результаты таким образом, чтобы показать правильное функционирование кода.

    Прекрасным механизмом для реализации тестовой структуры веб-службы являются веб-формы. Для метода Add класса Events была создана форма EventClient.aspx, реализующая добавление события. В веб-форму был добавлен элемент управления датой для фиксирования даты от конечного пользователя, и два текстовых поля для имени и описания события. Для отображения результирующего ID нового события после успешного выполнения функции Add в форму добавлено текстовое поле, отображающее ID, и кнопка Submit (Отправить), после нажатия на которую веб-форма выдает ответ. На рисунке 3.20 показан метод Add элемента управления датой после добавления события.

    (рис 3.20) Тестовая структура веб-формы с методом Add

    Код веб-формы EventClient.aspx, приведенный в листинге 3.10, несложен. Как видно из рисунка 3.20, для каждого свойства класса anEvent в форме присутствует элемент управления. Кнопка Submit используется для инициализации вызова события Add. Код веб-формы EventClient.aspx содержит событие нажатия на кнопку элемента управления btnAdd. Веб-форма EventClient.aspx должна выполняться в том же веб-приложении, что и веб-служба. Ее назначением является только модульное тестирование кода, поэтому форма максимально упрощена; она реально обеспечивает разработчика обратной связью, информирующей о производительности веб-службы Events.

    /// <summary>
    /// Summary description for EventClient.
    /// </summary>
    public class EventClient : System.Web.UI.Page
    {
         protected System.Web.UI.WebControls.Calendar Calendar1;
         protected System.Web.UI.WebControls.TextBox txtName;
         protected System.Web.UI.WebControls.Label Label1;
         protected System.Web.UI.WebControls.Label Label2;
         protected System.Web.UI.WebControls.TextBox txtDescription;
         protected System.Web.UI.WebControls.Label lblFeedback;
         protected System.Web.UI.WebControls.Button btnAdd;
    
         private void Page_Load(object sender, System.EventArgs e)
         {
              //don't show the feedback text box until
              //there is a good reason
              this.lblFeedback.Visible = false;
    
              //make the numbers red that are selected in calendar
              this.Calendar1.SelectedDayStyle.ForeColor =
                   System.Drawing.Color.Red;
         }
    
         #region Web Form Designer generated code
         override protected void OnInit(EventArgs e)
         {
         //
         // CODEGEN: This call is required by the 
         // ASP.NET Web Form Designer.
         //
           InitializeComponent();
              base.OnInit(e);
         }
         
         /// <summary>
         /// Required method for Designer support - do not modify
         /// the contents of this method with the code editor.
         /// </summary>
         private void InitializeComponent()
     {   
       this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
       this.Load += new System.EventHandler(this.Page_Load);
    
     }
         #endregion
    
         private void btnAdd_Click(object sender, System.EventArgs e)
         {
              Events myEvents = new Events();
              anEvent myEvent = new anEvent();
    
              //get the name
              myEvent.Name = this.txtName.Text;
    
              //get the date/time
              myEvent.DateTime = this.Calendar1.
                   SelectedDate.Date;
    
              //get the description
              myEvent.Description = this.txtDescription.Text;
              
              myEvents.Event = myEvent;
    
              //add it
              if (myEvents.Add())
              {
                   this.lblFeedback.Visible = true;
                   lblFeedback.Text = "Wrote a new date with ID = " +
                        myEvent.ID;
                   
              }
              else
              {
                   this.lblFeedback.Visible = true;
                   lblFeedback.Text = "Unable to write date";
              }
         }
    }

    Дизайнер компонентов автоматически добавил событие Click при двойном щелчке мышью на элементе управления btnAdd. В начале события Click созданы экземпляры класса anEvent и веб-службы Events. Так как эти классы находятся в едином пространстве имен, не нужно обращаться к пространству имен или размещать директиву using вверху исходного кода для пространства имен myPortal. Добавляемое событие представляется экземпляром anEvent с именем myEvent. Экземпляр класса Events называется myEvents. Свойства myEvent извлекаются из календарных элементов управления txtDescription и txtName. При заполнении пользователем экземпляра myEvent данными оно располагается в свойстве event экземпляра myEvents. Вызывается функция Add класса myEvents, и при ее успешной работе в элемент управления lblFeedback записывается сообщение об успехе функции; в противном случае – сообщение об ошибке.

    Следует заметить, что в данном коде не предусмотрена обработка ошибок, поскольку он предназначен для тестирования и не используется при функционировании веб-службы. Отсутствие системы обработки ошибок позволит получить больше данных для проверки кода на устойчивость к ошибкам, возникающим в процессе работы.

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

    Вернуться к учебному плану