Дмитрий Старостин
специалист по разработке программных систем московского представительства Microsoft
dmitrys@microsoft.com

Проблемы повторного использования программного обеспечения и интегрирования различных информационных систем возникли на заре развития ИТ-индустрии. Пройдя на пути решения этих задач от использования библиотек функций до объектно-ориентированного программирования и бинарных стандартов вызовов объектов, таких как COM и CORBA, ИТ-индустрия вплотную подошла к выработке стандартов в области механизмов взаимодействия систем. На смену вопросу "Чей клей для склеивания приложений лучше?" приходят вопросы: "Где взять универсальный клей?" и "Будет ли мой объект хорошо держаться универсальным клеем?" Какие механизмы и протоколы могут получить широкое распространение и будут приняты в качестве стандартов? Основанные на механизме RPC (Remote Procedure Call, вызов удаленной процедуры) бинарные протоколы несовместимы между собой, кроме того, они не могут решить задачи взаимодействия в распределенных Web-приложениях, поскольку только HTTP-трафик гарантированно переносит информацию через брандмауэры (Firewalls). Необходим иной подход.

Концепция Web Services решает задачу интеграции разнородных систем на основе открытых стандартов SOAP и XML, обеспечивая взаимодействие через Интернет объектов, реализованных в различных бинарных стандартах. Все это можно охарактеризовать, как движение Интернет-технологий от Web-браузеров к распределенным Web-приложениям.

SOAP представляет собой протокол обмена XML-сообщениями между объектами для передачи информации о вызываемых методах, параметрах и возвращаемых значениях. Он сам основан на языке XML, и формат его сообщений не зависит от транспортного протокола передачи сообщений. Вариант реализации SOAP, устанавливаемый поверх HTTP, находится на рассмотрении консорциума W3C. Уже сейчас разрабатываются реализации SOAP поверх других транспортных протоколов передачи сообщений, таких как SMTP и MSMQ.

Но для построения распределенных приложений недостаточно одного формата обмена. Нужен еще механизм описания предоставляемых объектом интерфейсов, реализованный на языке WSDL. Этот язык также сейчас рассматривается в W3C. Для перечисления Web-сервисов, предоставляемых данным Web-сервером, используется простейший протокол DISCO. Вместо использовавшегося в модели COM механизма RPC в Web-сервисах в качестве протокола вызовов объектов применяется SOAP, вместо WSDL как формата описания типов объектов - Type Library, вместо Registry - DISCO (как хранилище списка локально доступных объектов). Еще один механизм -UDDI - решает в Web-сервисах проблему глобального каталога.

На наших глазах концепция Web-сервисов переходит из стадии обсуждения и стандартизации в стадию реализации. Многие производители ПО заявили о планах или уже выпускают инструментальные средства для создания Web-сервисов. Microsoft.NET Framework и Visual Studio.NET, выпуск которых намечен на осень этого года, предоставляют разработчикам мощный инструментарий для создания XML Web-сервисов и построения распределенных Web-приложений.

Давайте рассмотрим процесс создания достаточно простого распределенного приложения, использующего Web-сервис. Приложение будет включать данные, реализованные в SQL Server, бизнес-логику, реализованную как Web-сервис, и пользовательский интерфейс, который мы реализуем в виде Windows-приложения с графическим интерфейсом пользователя и в виде приложения с тонким клиентом в браузере. Клиентские приложения будут обеспечивать просмотр и редактирование данных о продуктах из таблицы Products базы данных Northwind, входящей в поставку SQL Server 2000*.


*Эта база данных уже давно входит в качестве примера во многие продукты Microsoft, в том числе в Microsoft Office и Visaul Basic. Для работы с ней можно использовать более простые механизмы доступа к базе данных, в том числе ADO и DAO. - Прим. ред.

C базой данных клиентские приложения будут общаться через Web-сервис, используя протоколы HTTP и SOAP. Архитектура создаваемого нами распределенного приложения приведена на рис. 1.

Fig.N
Рис. 1. Архитектура распределенного Web-приложения в нашем примере.

Итак, поскольку искомая БД уже имеется в нашем распоряжении, начнем с уровня бизнес-логики, т.е. с Web-сервиса.

Создание Web-сервиса

Мы будем исходить из того, что программисты уже знакомы с технологией создания COM-бъектов для повторного использования. Простейший пример такой разработки с использованием VB 5.0 приведен, в частности, по адресу http://www.microsoft.com/rus/msdn/library/ в статье "Создание ActiveX-сервера". Поэтому сейчас мы попытаемся ответить на вопрос, насколько отличается разработка Web-сервиса в среде Visual Studio.NET от создания ActiveX DLL в Visual Basic 6.0. При этом для некоторого разнообразия покажем это на примере нового языка C# (вся логика разработки на VB.NET будет примерно той же).

Наш Web-сервис будет раскрывать два метода: GetProducts, позволяющий получить информацию о продуктах, и UpdateProducts, служащий для изменения информации в БД.

SOAP не ограничен передачей только простых типов данных и позволяет передавать в качестве параметров и возвращаемых значений структуры и массивы. Как нетрудно догадаться, сложные типы данных представляются в формате XML в теле SOAP-сообщений. Используя эту возможность при построении нашего Web-сервиса, мы будем возвращать объект типа DataSet (ADO.NET). Передача его через SOAP возможна в силу того, что он имеет интерфейс для представления своего состояния в XML-формате. Не рассказывая подробно об ADO.NET, отмечу, что через объект типа DataSet мы можем работать с наборами данных без постоянного соединения с источником данных. Фактически DataSet представляет собой СУБД, реализованную в памяти. Сформировав набор DataSet, передадим его на сторону клиента. После изменений набора данных клиентское приложение может передать DataSet обратно на уровень бизнес-логики, где и произойдет синхронизация с реальной базой данных. Начнем разработку.

  1. Запустите Visual Studio.NET.
  2. Через меню File| New| Project откройте диалоговое окно выбора типа нового проекта.
  3. Выберите Visual C# Projects (выбор языка, в конце концов, может быть делом вкуса) на панели Project Types и ASP.NET Web Service на панели Templates..
  4. Назовите проект NorthwindProductsWS и укажите http://localhost в качестве Web-сервера.
  5. Затем нажмите на OK, и Visual Studio создаст новый объект Solution с нашим проектом.
  6. Добавим два объекта к Web-сервису: SqlDataAdapter и SqlConnection. После соединения с БД посредством SqlConnection SqlDataAdapter выполнит запрос к таблице Products и заполнит данными объект DataSet. SqlDataAdapter будет также изменять БД на основе набора данных, возвращенного от пользователя.

  7. Откройте Server Explorer, выбрав пункт меню View | Server Explorer.
  8. Сделайте правый клик на Data Connections и выберите Add Connection из ниспадающего меню.
  9. В диалоговом окне Data Link Properties заполните информацию для соединения с базой данных Northwind.
  10. Нажмите OK, чтобы установить соединение. 10.
  11. В Server Explorer раскройте объект ServerName.Northwind.dbo и затем раскройте узел Tables. 11.
  12. Отбуксируйте мышью таблицу Products на панель дизайна. Пара SqlConnection и SqlDataAdapter, обеспечивающая соединение с источником данных, появится в нижней части панели дизайна (рис. 2). 12.
  13. В меню Data | Generate DataSet вызовите диалог, выберите пункт New и дайте набору данных имя ProductsDS1. 13.
  14. Сохраните файлы с помощью меню File | Save All.

Fig.2
Рис. 2. Среда проектирования Visual Studio.NET.

Теперь мы сделаем набор данных доступным для использования из Windows- или Web-приложений по протоколу SOAP. Для этого добавим к нашему Web-сервису методы. Web-сервис будем реализовывать в классе Service1, сгенерированном при создании проекта. При желании мы могли бы создать свой класс, наследовав его из System.Web.Services.WebService.

1. В Solution Explorer двойным щелчком мыши откройте Service1 и вызовите код через меню View | Code..

2. Добавьте метод для получения данных (на рис. 3 - новый мастер начального описания процедур). Метод будет создавать новый набор данных типа ProductsDS1 и заполнять его, используя SqlDataAdapter. После этого метод возвратит нужный набор данных.

Fig.3
Рис. 3. Новый мастер начального описания процедур.

[WebMethod]
      public ProductsDS1 GetProducts()
      {
         ProductsDS1 ds = new ProductsDS11();
         sqlDataAdapter1.Fill(ds);
         return ds;
    }

3. Добавьте метод для сохранения в базу данных изменений, произведенных клиентским приложением. Метод Update принимает изменения в наборе данных. Набор возвращается клиентскому приложению, которое отразит изменения в собственном экземпляре products1 dataset.

[WebMethod]
      public ProductsDS1 UpdateProducts(ProductsDS1 ds)
      {
         if( ds != null) 
         {
            sqlDataAdapter1.Update(ds);
            return ds;
         }
         else
         {
            return null;
         }
      }

4. Сохраните все файлы и постройте проект (меню Build | Build).

Итак, мы только что создали объект уровня бизнес-логики, методы которого доступны через SOAP-вызовы. От нас не потребовалось знания XML и SOAP - компилятор С# по атрибуту [WebMethod] знает, что наш объект должен быть доступен посредством SOAP. Посмотрим, в каком формате наш Web-сервис возвращает объект DataSet. Для этого запустим Internet Explorer и укажем адрес http://localhost/NorthwindProductsWS/Service1.asmx. ASP.NET на основе описания класса объекта, реализующего Web-сервис, сгенерирует страницу, с которой мы можем в интерактивном режиме вызывать методы Web-сервиса (рис. 4). Теперь в среде VS.NET вызовем с помощью команды Debug метод GetProducts. Появится начальная ASP-страница, на которой нужно выбрать ссылку на метод, чтобы перейти на страницу тестирования метода. На ней нажмем кнопку Invoke (рис. 5).

Fig.4
Рис. 4. Получилась вот такая ASP-страница.

Fig.5
Рис. 5. Тестирование созданного метода обращения к Web-сервису.

Как мы видим, XML-сообщение (рис. 6), в котором передается DataSet, содержит и описание структуры данных, и сами данные.

Fig.6
Рис. 6. XML-сообщение, полученное от Web-сервиса по нашему запросу.

Переходим к созданию Windows-приложения с графическим интерфейсом. Процесс его создания практически не будет отличаться от создания подобного приложения в Visual Basic 6.0.

Создание клиентского приложения с Windows-интерфейсом

  1. Вызовите меню File |Add Project и выберите New Project..
  2. В диалоговом окне Add New Project выберите Visual C# Projects на панели Project Types и Windows Application на панели Templates.
  3. Назовите проект ProductsWinClient.
  4. Form1 автоматически добавится к проекту и появится в визуальном дизайнере форм.

  5. Добавьте ссылку на наш Web-сервис. В Solution Explorer щелкните правой кнопкой мыши на проекте ProductsWinClient, выберите в контекстном меню Add Web Reference. Щелкните по ссылке Web References on Local Web Server. В появившемся списке выберите ссылку на NorthwindProductsWS. Нажмите Add Reference.

Теперь мы можем создавать экземпляры набора данных products1 в клиентском приложении.

Добавление ссылки на WebService очень похоже на добавление ссылки на библиотеку типов ActiveX в Visual Basic 6.0. Только информацию о типах в первом случае Visual Studio берет в формате WSDL, а во втором - в формате TypeLibrary. Попутно замечу, что ASP.NET генерирует на лету WSDL для нашего Web-сервиса, в чем можно убедиться, указав в Internet Explorer ссылку http://localhost/NorthwindProductsWS/Service1.asmx?WSDL.

Список доступных Web-сервисов Visual Studio получает из файла DISCO на Web-сервере.

Осталось создать интерфейс для работы с набором данных.

  1. Отбуксируйте на форму управляющий элемент DataGrid с панели Windows Forms из Toolbox..
  2. Добавьте на форму элемент Button, указав в ее свойстве Name "LoadData", а в свойстве Text - "Загрузить".
  3. Добавьте на форму еще одну кнопку Button, указав в ее свойстве Name "SaveData", а в свойстве Text - "Сохранить".
  4. Отбуксируйте на форму объект DataSet с панели Data из Toolbox. Откроется диалог Choose a DataSet. Выберите Typed dataset и укажите в списке Name "ProductsWinClient.ServerName.ProductsDS1". Visual Studio.NET создаст объект DataSet на основе определения класса productsDS1.
  5. Выберите DataSet control и установите свойство Name в ProductData.
  6. Выберите DataGrid control и укажите ProductData из списка в свойстве DataSource. Выберите products из списка в свойстве DataMember. Названиями колонок DataGrid будут названия колонок из таблицы Products.

Добавим код для кнопок

1. Двойным щелчком мыши на кнопке LoadData откройте пустой обработчик события Click. Для обращения к Web-сервису сначала создадим экземпляр объекта, а затем вызовем его методы. Возвращенный набор данных из метода GetProducts соединяется с ProductData dataset.

private void Load_Click(object sender, System.EventArgs e)
      {
      ProductsWinClient.localhost.Service1 ws = 
      	new ProductsWinClient.localhost.Service1();
         this.dataSet11.Merge(ws.GetProducts());
      }

2. Реализуйте обработчик события Click для кнопки Save.

Если в наборе данных есть изменения, для сохранения только что измененных данных создается новый набор типа productsDS1. Потом он передается в метод UpdateProducts нашего Web-сервиса. После возврата из метода он содержит принятые изменения и набор ProductData принимает эти изменения.

private void Save_Click(object sender, System.EventArgs e)
      {
         if( dataSet11.HasChanges())
         {

            ProductsWinClient.localhost.Service1 ws = 
            	new ProductsWinClient.localhost.Service1();
            ProductsWinClient.localhost.DataSet1 diffDS = 
            	new ProductsWinClient.localhost.DataSet1();

            diffDS.Merge(dataSet11.GetChanges());
            ws.UpdateProducts(diffDS);
            dataSet11.Merge(diffDS);
         }
      }

3. Выберите в Solution Explorer объект ProductsWinClient, нажмите правую кнопки мыши, в контекстном меню выберите StartUp Project.

4. Запустите приложение, нажав F5 (рис. 7).

Fig.7
Рис. 7. Мы создали Windows-приложение, которое обращается к Web-сервису.

Протестируйте наше клиентское приложение, нажимая на кнопки и внося изменения в данные.

Создание клиентского приложения с Web-интерфейсом

Построим теперь приложение с Web-интерфейсом и используем тот же самый объект уровня бизнес-логики.

5. Для создания Web-приложения выберите в диалоге создания нового проекта опцию ASP.NET Web Application.

6. Назовите проект ProductsWebClient. Страница с Web-формой (WebForm1.aspx) будет добавлена к проекту и загружена в дизайнер.

7. Добавьте ссылку на Web-сервис, точно так же, как сделали это для проекта с Windows-приложением.

8. Отбуксируйте элемент DataSet с панели Data из Toolbox на форму. В открывшемся диалоге Choose a DataSet выберите TypedDataSet и укажите "ProductsWebClient.ServerName.Products1" в списке Name. На форму будет добавлен элемент DataSet.

9. Для элемента DataSet установите ProductData в свойстве Name.

10. Отбуксируйте на форму элемент DataGrid с панели Web Forms из Toolbox.

11. В окне Properties для элемента DataGrid установите в свойстве DataSource "ProductData", а в свойстве DataMember - "products". Эти установки должны предлагаться в ниспадающих списках. Названиями колонок DataGrid будут названия колонок из таблицы Products.

12. Для реализации редактирования на месте в объекте DataGrid добавьте колонку Edit, Update, Cancel, которая будет содержать кнопку редактирования. Когда пользователь нажмет кнопку редактирования, содержимое строки будет отображено в элементе TextBox (отдельно для каждой колонки), и кнопка редактирования будет заменена на кнопки Update и Cancel. Вот как это сделать:

Щелкните на ссылке Property Builder внизу окна Properties и выберите в диалоговом окне панель Columns. Раскройте узел Button Column в панели Available Columns. Выберите Edit, Update, Cancel и нажмите кнопку Add, а затем OK.

Добавим код для кнопок. Нажмите правую кнопку мыши, выберите в контекстном меню View Code. Добавьте код к событию Page_Load. В этом фрагменте мы создадим экземпляр нашего Web-сервиса, заполним набор данных ProductData и свяжем его с элементом DataGrid. Каждый раз, когда страница направляется клиенту в ответ на запрос, таблица будет содержать свежую копию данных из базы данных.

private void Page_Load(object sender, System.EventArgs e)
      {
         // Put user code to initialize the page here
         
         ProductsWebClient.localhost.Service1 ws = 
         	new ProductsWebClient.localhost.Service1();
         this.dataSet11.Merge(ws.GetProducts());
         if(!Page.IsPostBack)
         {
            DataGrid1.DataBind();
         }

      }

Когда пользователь нажимает кнопку Edit, генерируется событие EditCommand объекта DataGrid. Используем это событие для изменения индекса EditItemIndex в DataGrid. Строка с указанным индексом отобразится в виде набора TextBox.

Создадим обработчик событий для EditCommand. В окне свойств для DataGrid нажмите на кнопку Events и отобразите список событий DataGrid. Добавьте код к событию EditCommand:

private void DataGrid1_EditCommand(object source, 
System.Web.UI.WebControls.DataGridCommandEventArgs e)
      {
         DataGrid1.EditItemIndex = e.Item.ItemIndex;
         DataGrid1.DataBind();
      }

Создайте обработчик ошибок для события CancelCommand:

private void DataGrid1_CancelCommand(object source, 
System.Web.UI.WebControls.DataGridCommandEventArgs e)
      {
         DataGrid1.EditItemIndex = -1;
         DataGrid1.DataBind();
      }

Когда пользователь нажимает на кнопку Cancel, генерируется событие CancelCommand объекта DataGrid. Установим в коде для этого события EditItemIndex равным -1, так что текущая строка будет отображаться снова в виде набора TextBox.

Когда пользователь нажимает на Update, генерируется событие UpdateCommand объекта DataGrid. В этом событии мы должны получить набор данных ProductData с изменениями из DataGrid и провести эти изменения обратно в базу данных через обращение к Web-сервису:

private void DataGrid1_UpdateCommand(object source, 
System.Web.UI.WebControls.DataGridCommandEventArgs e)
      {
         for( int i = 0; i< DataGrid1.Columns.Count-1; i++)
         {
            TextBox tb = (TextBox)e.Item.Cells[i].Controls[0];
            
            DataRow row = dataSet11.products[e.Item.DataSetIndex];
            row[DataGrid1.Columns[i].HeaderText] = tb.Text;
         
         }
         if(dataSet11.HasChanges()) 
         {
ProductsWebClient.localhost.Service1 ws = 
	new ProductsWebClient.localhost.Service1();
ProductsWebClient.localhost.DataSet1 ds = 
	new ProductsWebClient.localhost.DataSet1();
            ds.Merge(dataSet11.GetChanges());
            ws.UpdateProducts(ds);
            dataSet11.Merge(ds);
         }
         DataGrid1.EditItemIndex = -1;
         DataGrid1.DataBind();

      }

Выберите ProductsWebClient в Solution Explorer, нажмите правую кнопку мыши и определите проект как Set as StartUp Project. Запустите приложение, нажав F5.

Отметим, что создание Web-приложений с использованием ASP.NET происходит аналогично созданию Windows-приложений. Мы отдельно работали с формой нашей страницы и отдельно писали код для обработки ошибок. Ничто не напоминало "кашу" ASP-страниц с перемешанными HTML-тегами и кусками скриптов.

Помимо Visual Studio.NET Microsoft предлагает и другие инструменты для создания и использования Web-сервисов. SOAP Toolkit можно использовать вместе с Visual Studio 6.0. SOAP Toolkit предоставляет объекты для генерирования proxy, которые берут на себя уровень взаимодействия по SOAP, так что вы обращаетесь к Web-сервисам как к обычным COM-объектам. SOAP Toolkit содержит два набора интерфейсов - высокоуровневый и низкоуровневый. С их помощью вы можете влиять на формирующиеся SOAP-сообщения. Дополнительно к SOAP Toolkit можно использовать Web Service Proxy Wizard, который добавляется как надстройка (Add-On) к среде Visual C++ 6.0. Web Service Proxy Wizard генерирует класс на основе Web-сервисов и библиотеку типов для этого класса. Это дает возможность использовать раннее связывание в проектах в Visual Basic 6.0 при обращении к Web-серсиву. Сгенерированный класс работает с SOAP Toolkit посредством низкоуровневого интерфейса. Кроме того, для Internet Explorer доступен HTC-компонент, который позволяет работать с Web-сервисами напрямую из DHTML на стороне браузера. Все эти дополнительные средства доступны для загрузки на http://msdn.microsoft.com в разделе Downloads.

Как мы только что убедились, создание и использование Web-сервисов при разработке в среде Visual Studio.NET мало чем отличается от работы с COM-объектами и дает нам возможность построения распределенных приложений, не ограниченных рамками корпоративной сети. Значит ли это, что Web-сервисы нужно использовать при любом удаленном взаимодействии объектов? Выскажу свою точку зрения. Являясь действительно простым протоколом, SOAP имеет и свои естественные ограничения. Он не обеспечивает поддержку распределенных транзакций, влечет дополнительные расходы на упаковку в XML-формат и распаковку данных. SOAP и Web-сервисы полностью не вытеснят удаленные взаимодействия объектов через бинарные протоколы, а скорее дополнят их, прежде всего при построении внешних интерфейсов во внутренние системы компаний, портальных решений B2B, распределенных систем со взаимодействием модулей через Интернет, публичных, ориентированных на использование частными клиентами услуг и приложений.

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

1. Удаленное взаимодействие модулей системы (классический "удаленный склад").

2. Портал дистрибьютора. Партнерам дается возможность не только работать через Web-интерфейс, но и напрямую интегрировать их учетные системы через Интернет по протоколу SOAP с вашей системой. Использование при этом стандартных XML-сообщений, например CommerceML, которые поддерживаются в тиражируемом программном обеспечении ведущих российских производителей ПО, позволит многим вашим контрагентам осуществить такую интеграцию без особых усилий.

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