Опубликован: 24.11.2006 | Уровень: специалист | Доступ: платный
Лекция 3:

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

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Компонент 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). Это окно при желании можно закрыть, а свойства настроить вручную.

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

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

    Рис. 3.8. Мастер настройки адаптера данных: выбор подключения к данным
  3. После выбора подключения нажмите на кнопку 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 (Использовать имеющиеся сохраненные процедуры), и мастер запросит дополнительную информацию о таблицах данных, необходимую для построения и связи сохраненных процедур.

  4. В нашем примере выберите опцию Use SQL Statements.
  5. Нажмите на кнопку Next (Далее) для перехода в окно Generate The SQL Statements (Генерация выражений SQL). Введите команду Select SQL для набора данных, используемых адаптером при построении выражений SQL (см. рис. 3.10). SQLDataAdaptor и OledbDataAdaptor поддерживают интерфейс для выполнения операторов Select (Выбор), Insert (Вставка), Delete (Удаление) и Update (Обновление). Мастер генерирует команды SQL для управления данными, устанавливаемыми в адаптере данных. Он запрашивает выражение SQL Select для создания текста команд Select, Insert, Delete и Update.
    Мастер настройки адаптера данных: генерация выражений SQL

    Рис. 3.10. Мастер настройки адаптера данных: генерация выражений SQL
  6. После ввода в поле оператора 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. Initialization Code Generated by the VS Designer for a Data Adaptor and a Data Connection

В листинге 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"));
Листинг 3.6. Initialization Code Generated by the VS Designer for a InsertEvent SQLCommand Object in Subroutine InitializeComponent();
Класс 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;
}
Листинг 3.7. Add Function in Events Web Service

Экземпляр класса 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. Source Code for Class anEvent

Как видно из листинга 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;
}
Листинг 3.9. LogMessage Function of the Events Class

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

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

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

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Дмитрий Васюков
Дмитрий Васюков
Россия, Брянск
Maxim Kuzmin
Maxim Kuzmin
Россия