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 автоматически по первому запросу.
Для демонстрации процесса создания веб-службы в данной лекции представлено простое приложение для составления расписания по датам. Рассматриваемая система расписания состоит из трех следующих классов.
anEvent.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.
Service1. В нашем примере проект называется myPortal (см. рис. 3.2).
Задайте путь к корневому веб-каталогу сервера при помощи адреса URL или
UNC. В URL указывается имя сервера по умолчанию localhost, если
разрабатываемое программное решение располагается на рабочей станции
при написании кода и модульном тестировании.Visual Studio .NET выполнит подключение к веб-серверу, указанному в диалоговом окне New Project (Новый проект), создаст новый виртуальный каталог и скопирует в него файлы веб-служб по умолчанию.
После генерации файлов проекта отобразится представление Design
(Дизайн) веб-службы Service1, готовое к разработке. Visual Studio .NET
присваивает веб-файлам веб-службы имена по умолчанию, поэтому их нужно
переименовать.
Service1.asmx и выберите Rename
(Переименовать).Service1 и введите имя Events.Events.asmx, Events.asmx.cs и Events.asmx.res
соответственно.Класс, обеспечивающий работу веб-службы, будет по-прежнему называться Service1. Веб-служба исправно функционирует с различными именами, но мы
все-таки сменим имя класса, присвоив ему имя службы.
Events.asmx в диспетчере Solution Explorer и нажмите на
кнопку View Designer (Отобразить дизайнер). Файл Events.asmx.cs
отобразится в дизайнере компонентов).Events.asmx.cs и выберите
Properties (Свойства).Service1.
Удалите это значение.Веб-служба 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, отличие же заключается в следующем.
Команда #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, поэтому добавим компонент . При поддержке другого
типа базы данных используется компонент OleDBConnection. Компонент дает дополнительные возможности по управлению и повышает
эффективности работы, в отличие от компонента 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. После настройки и создания
подключение работает в любом месте веб-службы. Класс 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,
как и другие переменные и классы. Например, экземпляр объекта можно создать и инициализировать в конструкторе класса Events. Код, приведенный в листинге 3.3, демонстрирует создание
локального экземпляра объекта без использования дизайнера
компонентов и компонента .
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 великолепно подходит для получения инициализационных
данных, индивидуальных для веб-приложения. Это позволяет создавать
решение программно, чтобы получить аргументы, связанных с конкретной
реализацией, без внесения изменений в код других реализаций. С помощью
класса 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 с ключом в файле
конфигурации
После установки подключения веб-служба узнает источник данных и
аутентификационные данные, необходимые для предоставления этому
источнику. Далее веб-служба отправляет запрос источнику данных на
получение данных. ADO.NET предлагает для этого еще один компонент – адаптер данных (data adaptor). Версия адаптера данных для SQL Server
2000 использует провайдер SQL Server .NET с именем SQLDataAdaptor. OledbDataAdaptor (версия для OLE-DB) работает с предыдущими версиями
SQL Server или базами данных, не имеющих конкретного провайдера.
Адаптер данных можно установить, настроить и применить, как и любой
другой класс в ADO.NET. ASP.NET содержит в инструментарии
альтернативный компонент, который можно использовать в случае
необходимости. Адаптер данных добавляется в дизайнере компонентов
веб-службы и настраивается в окне свойств.
При добавлении компонента открывается окно мастера настройки адаптера данных (Data Adaptor Configuration Wizard). Это окно при желании можно закрыть, а свойства настроить вручную.
Ниже приведены инструкции по работе с мастером настройки адаптера данных.
(рис 3.8) Мастер настройки адаптера данных: выбор подключения к даннымПосле выбора подключения нажмите на кнопку Next (Далее), и мастер предложит выбрать метод запроса данных из источника в окне Choose A Query Type (Выбор тип запроса) (см. рис. 3.9).
(рис 3.9) Мастер настройки адаптера данных: выбор типа запросаВыберите Use SQLCommand или OledbCommand ) в функции InitializeComponent класса веб-службы.
Командные объекты будут созданы с набором SQL-команд, которые
используют псевдонимы каждого имени поля. С помощью ссылки на псевдоним
в экземпляре командного объекта можно установить значения
рассматриваемого поля.
Выберите Create New
SQLDataAdaptor и OledbDataAdaptor поддерживают интерфейс
для выполнения операторов Select (Выбор), Insert (Вставка), Delete
(Удаление) и Update (Обновление). Мастер генерирует команды SQL для
управления данными, устанавливаемыми в адаптере данных. Он запрашивает
выражение SQL Select для создания текста команд Select, Insert, Delete
и (рис 3.10) Мастер настройки адаптера данных: генерация выражений SQL
(рис 3.11) Мастер настройки адаптера данных: просмотр результатов работыВ нашем примере адаптер данных называется SQLDataAdaptor1, так как
дизайнер компонентов присваивает это имя по умолчанию любому компоненту SQLDataAdaptor, добавляемому в представлении Design (Дизайн). Это имя
можно изменить в любой момент, и сгенерированный мастером код в
подпрограмме InitializeComponent веб-службы автоматически обновится. В
нашем примере имя SQLDataAdaptor изменено на EventsAdaptor в окне
свойств.
Объектам SQLCommand, сгенерированным мастером настройки адаптера
данных, присвоены имена SQLSelectCommand1, SQLDeleteCommand1, SQLInsertCommand1 и SQLUpdateCommand1. Имена командных объектов
заменены на SelectEvent, DeleteEvent, InsertEvent и UpdateEvent
соответственно. Это было сделано в окне свойств SQLDataAdaptor, т.е.
изменено свойство имени, подчиненное 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"));
С учетом псевдонимов, сгенерированных дизайнером компонентов, можно
разработать код, устанавливающий параметры и заполняющий объект 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 этой проблемы не существует, так
как порядковая позиция определяется в терминах экземпляра адаптера
данных. Адаптер данных обеспечивает определенный уровень
абстрагирования от уровня данных, поэтому программист может обращаться
к порядковой позиции, не опасаясь, что изменения в таблице базы данных
повредят программное обеспечение.
Весь код взаимодействия с базой данных в листинге 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 являются две
встроенные учетные записи для рабочих процессов:
Для настройки приложения на работу под другой учетной записью следует создать новый пул приложения и настроить его на работу под аутентификационными данными учетной записи Local System (Локальная система).
DefaultAppPool. При его развертывании отобразятся
веб-сайты, использующие этот элемент (см. рис. 3.12).Default Web Site, созданный при установке IIS,
использует пул DefaultAppPool. Как видно из рисунка 3.12, сайт Default
Web Site присутствует в списке многих веб-приложений, использующих DefaultAppPool. Для настройки пула приложения откройте страницу свойств
веб-сайта или виртуального каталога, щелкнув правой кнопкой мыши на
узле и выбрав команду Properties (Свойства).
(рис 3.12) Приложения, использующие DefaultAppPool, отображаются в
консоли MMC Откройте вкладку Home Directory (Домашний каталог) веб-сайта либо вкладку Virutal Directory (Виртуальный каталог) виртуального каталога. Во вкладке Home Directory (Домашний каталог) поле со списком Application Pool (Пул приложения) отображает доступные пулы приложения (см. рис. 3.13).
(рис 3.13) Вкладка Home Directory (Домашний каталог) окна свойств веб-сайта по умолчаниюВеб-служба Events работает в виртуальном каталоге myPortal,
расположенном на сайте Default Web Site. Виртуальный каталог myPortal
настроен на использование пула DefaultAppPool, работающего под
аутентификационными данными учетной записи Events попытается записать данные в журнал
событий, произойдет ошибка, так как учетная запись Events осуществляется без всяких
проблем. Для разрешения проблемы доступа создайте новый пул приложения,
использующий учетную запись Local System, и настройте myPortal на
работу с новым пулом приложения.
webservice using LocalSystem. В нашем случае
используются параметры по умолчанию нового пула приложения.
(рис 3.14) Диалоговое окно Add New Application Pool (Создание нового пула приложения)Ниже приведены шаги по смене объекта пула приложения webservice using
LocalSystem.
webservice using
LocalSystem в консоли MMC
(рис 3.15) Настройка учетной записи для пула приложенияПосле создания пула приложения виртуальный каталог 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 автоматически по первому запросу.
Для демонстрации процесса создания веб-службы в данной лекции представлено простое приложение для составления расписания по датам. Рассматриваемая система расписания состоит из трех следующих классов.
anEvent.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.
Service1. В нашем примере проект называется myPortal (см. рис. 3.2).
Задайте путь к корневому веб-каталогу сервера при помощи адреса URL или
UNC. В URL указывается имя сервера по умолчанию localhost, если
разрабатываемое программное решение располагается на рабочей станции
при написании кода и модульном тестировании.Visual Studio .NET выполнит подключение к веб-серверу, указанному в диалоговом окне New Project (Новый проект), создаст новый виртуальный каталог и скопирует в него файлы веб-служб по умолчанию.
После генерации файлов проекта отобразится представление Design
(Дизайн) веб-службы Service1, готовое к разработке. Visual Studio .NET
присваивает веб-файлам веб-службы имена по умолчанию, поэтому их нужно
переименовать.
Service1.asmx и выберите Rename
(Переименовать).Service1 и введите имя Events.Events.asmx, Events.asmx.cs и Events.asmx.res
соответственно.Класс, обеспечивающий работу веб-службы, будет по-прежнему называться Service1. Веб-служба исправно функционирует с различными именами, но мы
все-таки сменим имя класса, присвоив ему имя службы.
Events.asmx в диспетчере Solution Explorer и нажмите на
кнопку View Designer (Отобразить дизайнер). Файл Events.asmx.cs
отобразится в дизайнере компонентов).Events.asmx.cs и выберите
Properties (Свойства).Service1.
Удалите это значение.Веб-служба 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, отличие же заключается в следующем.
Команда #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, поэтому добавим компонент . При поддержке другого
типа базы данных используется компонент OleDBConnection. Компонент дает дополнительные возможности по управлению и повышает
эффективности работы, в отличие от компонента 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. После настройки и создания
подключение работает в любом месте веб-службы. Класс 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,
как и другие переменные и классы. Например, экземпляр объекта можно создать и инициализировать в конструкторе класса Events. Код, приведенный в листинге 3.3, демонстрирует создание
локального экземпляра объекта без использования дизайнера
компонентов и компонента .
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 великолепно подходит для получения инициализационных
данных, индивидуальных для веб-приложения. Это позволяет создавать
решение программно, чтобы получить аргументы, связанных с конкретной
реализацией, без внесения изменений в код других реализаций. С помощью
класса 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 с ключом в файле
конфигурации
После установки подключения веб-служба узнает источник данных и
аутентификационные данные, необходимые для предоставления этому
источнику. Далее веб-служба отправляет запрос источнику данных на
получение данных. ADO.NET предлагает для этого еще один компонент – адаптер данных (data adaptor). Версия адаптера данных для SQL Server
2000 использует провайдер SQL Server .NET с именем SQLDataAdaptor. OledbDataAdaptor (версия для OLE-DB) работает с предыдущими версиями
SQL Server или базами данных, не имеющих конкретного провайдера.
Адаптер данных можно установить, настроить и применить, как и любой
другой класс в ADO.NET. ASP.NET содержит в инструментарии
альтернативный компонент, который можно использовать в случае
необходимости. Адаптер данных добавляется в дизайнере компонентов
веб-службы и настраивается в окне свойств.
При добавлении компонента открывается окно мастера настройки адаптера данных (Data Adaptor Configuration Wizard). Это окно при желании можно закрыть, а свойства настроить вручную.
Ниже приведены инструкции по работе с мастером настройки адаптера данных.
(рис 3.8) Мастер настройки адаптера данных: выбор подключения к даннымПосле выбора подключения нажмите на кнопку Next (Далее), и мастер предложит выбрать метод запроса данных из источника в окне Choose A Query Type (Выбор тип запроса) (см. рис. 3.9).
(рис 3.9) Мастер настройки адаптера данных: выбор типа запросаВыберите Use SQLCommand или OledbCommand ) в функции InitializeComponent класса веб-службы.
Командные объекты будут созданы с набором SQL-команд, которые
используют псевдонимы каждого имени поля. С помощью ссылки на псевдоним
в экземпляре командного объекта можно установить значения
рассматриваемого поля.
Выберите Create New
SQLDataAdaptor и OledbDataAdaptor поддерживают интерфейс
для выполнения операторов Select (Выбор), Insert (Вставка), Delete
(Удаление) и Update (Обновление). Мастер генерирует команды SQL для
управления данными, устанавливаемыми в адаптере данных. Он запрашивает
выражение SQL Select для создания текста команд Select, Insert, Delete
и (рис 3.10) Мастер настройки адаптера данных: генерация выражений SQL
(рис 3.11) Мастер настройки адаптера данных: просмотр результатов работыВ нашем примере адаптер данных называется SQLDataAdaptor1, так как
дизайнер компонентов присваивает это имя по умолчанию любому компоненту SQLDataAdaptor, добавляемому в представлении Design (Дизайн). Это имя
можно изменить в любой момент, и сгенерированный мастером код в
подпрограмме InitializeComponent веб-службы автоматически обновится. В
нашем примере имя SQLDataAdaptor изменено на EventsAdaptor в окне
свойств.
Объектам SQLCommand, сгенерированным мастером настройки адаптера
данных, присвоены имена SQLSelectCommand1, SQLDeleteCommand1, SQLInsertCommand1 и SQLUpdateCommand1. Имена командных объектов
заменены на SelectEvent, DeleteEvent, InsertEvent и UpdateEvent
соответственно. Это было сделано в окне свойств SQLDataAdaptor, т.е.
изменено свойство имени, подчиненное 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"));
С учетом псевдонимов, сгенерированных дизайнером компонентов, можно
разработать код, устанавливающий параметры и заполняющий объект 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 этой проблемы не существует, так
как порядковая позиция определяется в терминах экземпляра адаптера
данных. Адаптер данных обеспечивает определенный уровень
абстрагирования от уровня данных, поэтому программист может обращаться
к порядковой позиции, не опасаясь, что изменения в таблице базы данных
повредят программное обеспечение.
Весь код взаимодействия с базой данных в листинге 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 являются две
встроенные учетные записи для рабочих процессов:
Для настройки приложения на работу под другой учетной записью следует создать новый пул приложения и настроить его на работу под аутентификационными данными учетной записи Local System (Локальная система).
DefaultAppPool. При его развертывании отобразятся
веб-сайты, использующие этот элемент (см. рис. 3.12).Default Web Site, созданный при установке IIS,
использует пул DefaultAppPool. Как видно из рисунка 3.12, сайт Default
Web Site присутствует в списке многих веб-приложений, использующих DefaultAppPool. Для настройки пула приложения откройте страницу свойств
веб-сайта или виртуального каталога, щелкнув правой кнопкой мыши на
узле и выбрав команду Properties (Свойства).
(рис 3.12) Приложения, использующие DefaultAppPool, отображаются в
консоли MMC Откройте вкладку Home Directory (Домашний каталог) веб-сайта либо вкладку Virutal Directory (Виртуальный каталог) виртуального каталога. Во вкладке Home Directory (Домашний каталог) поле со списком Application Pool (Пул приложения) отображает доступные пулы приложения (см. рис. 3.13).
(рис 3.13) Вкладка Home Directory (Домашний каталог) окна свойств веб-сайта по умолчаниюВеб-служба Events работает в виртуальном каталоге myPortal,
расположенном на сайте Default Web Site. Виртуальный каталог myPortal
настроен на использование пула DefaultAppPool, работающего под
аутентификационными данными учетной записи Events попытается записать данные в журнал
событий, произойдет ошибка, так как учетная запись Events осуществляется без всяких
проблем. Для разрешения проблемы доступа создайте новый пул приложения,
использующий учетную запись Local System, и настройте myPortal на
работу с новым пулом приложения.
webservice using LocalSystem. В нашем случае
используются параметры по умолчанию нового пула приложения.
(рис 3.14) Диалоговое окно Add New Application Pool (Создание нового пула приложения)Ниже приведены шаги по смене объекта пула приложения webservice using
LocalSystem.
webservice using
LocalSystem в консоли MMC
(рис 3.15) Настройка учетной записи для пула приложенияПосле создания пула приложения виртуальный каталог 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 записывается
сообщение об успехе функции; в противном случае – сообщение об ошибке.
Следует заметить, что в данном коде не предусмотрена обработка ошибок, поскольку он предназначен для тестирования и не используется при функционировании веб-службы. Отсутствие системы обработки ошибок позволит получить больше данных для проверки кода на устойчивость к ошибкам, возникающим в процессе работы.
Тестовую структуру следует содержать и хранить точно так же, как исходный код веб-службы. Она отражает работу программного решения и используется при проверке дальнейших усовершенствований веб-службы.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.