Работа с WS-ссылками в дизайнере ELMA

В ходе реализации интеграционного взаимодействия ELMA и других информационных систем часто встречаются случаи, когда на стороне сторонней системы развернут WEB-сервис, описанный по правилам WSDL (Web Services Description Language).

Описание основано на языке XML и представляет из себя структуру запросов/ответов сервера и форматов данных.

Пример общедоступного WSDL без авторизации – информационный сервис Центрального Банка России.

В системе ELMA реализован сервис для запуска бизнес-процессов из внешних систем (примеры его использования приведены в статьях Базы знаний https://www.elma-bpm.ru/KB/article-5740.html и https://www.elma-bpm.ru/KB/article-5806.html): <АдресСервераELMA>/Modules/EleWise.ELMA.Workflow.Processes.Web/WFPWebService.asmx?WSDL.

Дизайнер ELMA позволяет инициализировать правильно описанный WSDL как новый класс в рамках модуля сценариев. При этом использовать такой сервис можно без необходимости дополнительных инициализаций WEB-запросов.

Рассмотрим несколько примеров работы с WSDL. Они отличаются особенностями взаимодействия и обработкой результата.

Получение курса валют ЦБ РФ на текущую дату

В модуле сценариев процесса получим все курсы валют Центрального банка и выведем их в консоль в виде простого текста с разделителями. Используем для этого описанный выше сервис.

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

Рис. 1. Добавление службы

После этого откроется диалог, в котором необходимо указать адрес до WEB-сервиса и задать нужное пространство имен для импортируемого класса. После нажатия кнопки Перейти мы также можем увидеть, какие методы реализованы на стороне сервиса. Пространство имен не должно совпадать с уже существующими в системе. Пример заполнения диалога и порядок действий представлены на рис. 2:

Рис. 2. Настройки импорта WS-ссылки

Для работы необходимо использовать пространства имен:

using System.ServiceModel;
using System.Data;

Также необходимо подключить сборку System.Data.

Текст процедуры:

public void GetCBCourses()
{
    //инициализируем необходимые параметры
    BasicHttpBinding myBinding = new BasicHttpBinding();
    //адрес должен совпадать с адресом добавляемой WS-ссылки
    EndpointAddress address = new EndpointAddress("http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?WSDL");
    //для наглядности - в коде указано обращение к добавленному классу по полному пути
    //в сервисах почти всегда присутствует класс с именем, содержащим "SoapClient" или что-то подобное, он и будет являться основным сервисом для вызова остальных методов
    //инициализация обязательно должна быть с указанием инициализированных выше параметров (настройки протокола и адреса)
    EleWise.ELMA.Model.Scripts.CBRru.DailyInfoSoapClient newWFP = new EleWise.ELMA.Model.Scripts.CBRru.DailyInfoSoapClient(myBinding,address);        
    //Используем метод сервиса, который возвращает информацию в виде DataSet - это структура, содержащая набор таблиц данных
    //метод GetCursOnDateXML позволяет вернуть курс в виде XML
    DataSet cursSet = newWFP.GetCursOnDate(DateTime.Now);
    //обычно в документации к сервису указано, в каких полях какие данные хранятся, однако для примера разберем полный обход каждой таблицы из полученной структуры
    foreach(DataTable dTable in cursSet.Tables)
    {
        Console.WriteLine("Имя таблицы: "+dTable.TableName);
        //выводим названия колонок таблицы
        foreach(DataColumn tColumn in dTable.Columns)
        {
            Console.Write(tColumn.ColumnName+" | ");
        }
        //перевод строки
        Console.WriteLine("");
        //для каждой строки выводим значения из всех колонок
        foreach(DataRow tRow in dTable.Rows)
        {
            for(int i = 0; i < dTable.Columns.Count; i++)
            {
                Console.Write(tRow[i].ToString().Trim()+" | ");
            }
            //перевод строки
            Console.WriteLine("");
            
        }
    } 
}

Запуск процесса через WS с параметрами

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

Рассмотрим такое взаимодействие на примере запуска процесса ELMA через специальный сервис, который был упомянут в начале данной статьи. Для примера мы используем запуск процесса в той же системе, откуда выполняется сценарий. Однако через такое взаимодействие можно организовать запуск на другом экземпляре ELMA.

Аналогично добавляем ссылку на сервис. В нашем случае ELMA установлена по адресу localhost:8000 (рис. 3).

Рис. 3. Настройка сервиса запуска процессов

О том, как настраивается процесс для запуска через WEB-сервис, написано в статье https://www.elma-bpm.ru/KB/article-5740.html (генерация токена и необходимые флаги настроек), в рамках данной статьи это не рассматривается.

Запустим процесс при помощи сценария и установим значение переменной контекста Описание.

public void RunProcWithWSDL()
{
    //инициализируем экземпляры классов, описанных в сервисе
    //WebData - это массив элементов для передачи
    EleWise.ELMA.Model.Scripts.WFPRun.WebData newWebDt = new EleWise.ELMA.Model.Scripts.WFPRun.WebData();
    //WebDataItem - элемент для передачи, представляет из себя связку: "Имя"-"Значение", где "Имя" совпадает с наименованием переменной контекста запускаемого процесса
    EleWise.ELMA.Model.Scripts.WFPRun.WebDataItem newWebDtItm = new EleWise.ELMA.Model.Scripts.WFPRun.WebDataItem();
    //Отдельно инициализируем список WebDataItem, который потом преобразуем в массив
    List<EleWise.ELMA.Model.Scripts.WFPRun.WebDataItem> newWebDtItmArr = new List<EleWise.ELMA.Model.Scripts.WFPRun.WebDataItem>();
    //задаем параметры запускаемого процесса, в контексте у нас имеется переменная "Description" ("Описание")
    newWebDtItm.Name = "Description";
    newWebDtItm.Value = "Запуск из сценария через WEB-сервис";
    newWebDtItmArr.Add(newWebDtItm);
    //если переменных несколько - их необходимо по-очереди добавить в newWebDtItmArr
    newWebDt.Items = newWebDtItmArr.ToArray();
    //параметры для запуска методов сервиса
    BasicHttpBinding myBinding = new BasicHttpBinding();
    EndpointAddress address = new EndpointAddress("http://localhost:8000/Modules/EleWise.ELMA.Workflow.Processes.Web/WFPWebService.asmx?WSDL");
    //аналогично инициализируем основной класс WEB-сервиса
    EleWise.ELMA.Model.Scripts.WFPRun.WFPWebServiceSoapClient newWFP = new EleWise.ELMA.Model.Scripts.WFPRun.WFPWebServiceSoapClient(myBinding,address);
    //параметры метода: "Логин","Пароль","Токен процесса","Название экземпляра","Массив WebData"
    //название экземпляра не указываем, так как используется механизм наименования экземпляров по шаблону
    var rslt = newWFP.Run("userlogin","userpwd","4a489e0b-e4da-4799-9a5a-c0f353d7925f","",newWebDt);
    //выводим ИД запущенного экземпляра
    Console.WriteLine(rslt.ToString());
}

После выполнения сценария мы получаем ИД запущенного экземпляра (рис. 4).

Рис. 4. Результаты работы сценария

На странице информации об экземпляре процесса проверим заполнение необходимой контекстной переменной (рис. 5).

Рис. 5. Информация о запущенном процессе

Получение данных от сервиса, требующего аутентификацию

Если к информации, которую можно получить при помощи WEB-сервиса, требуется ограничить доступ, сервис использует параметры аутентификации.

Рассмотрим пример получения данных о таблицах информационной системы Microsoft Navision 2017 через реализованный на ее стороне WEB-сервис. В целях сохранения конфиденциальности в рамках статьи – адреса и имена серверов будут скрыты.

В этом случае на этапе добавления ссылки будет выведен диалог ввода логина и пароля. Ссылка добавится после ввода учетных данных (рис. 6).

Рис. 6. Параметры авторизации WEB-сервиса

Инициализация сервиса в программном коде также будет отличаться. В частности, потребуется дополнительное описание параметров авторизации.

public virtual void GetInfoFromNavision (Context context)
{
    //инициализируем параметры
    BasicHttpBinding myBinding = new BasicHttpBinding();
    //в случае использования авторизации необходимо указать дополнительные параметры безопасности
    myBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
    //то, какой тип аутентификации необходимо уточнить у администратора системы
    myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
    //адрес также должен совпадать с адресом добавляемой ссылки (в примере скрыты реальные адреса)
    EndpointAddress address = new EndpointAddress("http://adressHide.net:7117/adressHide_Oper2/WS/adressHide/Page/UMD_TablesList");
    //в данном случае в названии основного класса-сервиса есть слово "Client"
    EleWise.ELMA.Model.Scripts.NavisionTables.UMD_TablesList_PortClient navClien = new EleWise.ELMA.Model.Scripts.NavisionTables.UMD_TablesList_PortClient(myBinding,address);
    //указываем параметры аутентификации (те же данные, что и во время добавления ссылки)
    navClien.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("userlogin", "userpws");
    //мы будем вызывать метод получения всех таблиц системы
    //для этого метода требуется передать параметры, которые являются типами, описанными в WSDL
    //инициализируем пустой массив-фильтр объектов
    List<EleWise.ELMA.Model.Scripts.NavisionTables.UMD_TablesList_Filter> tabFilt = new List<EleWise.ELMA.Model.Scripts.NavisionTables.UMD_TablesList_Filter>();
    var tablFiltArray = tabFilt.ToArray();
    //получаем коллекцию записей таблицы, параметры метода: "таблица-фильтр","строка-фильтр","количество выбираемых записей" 
    var tabl = navClien.ReadMultiple(tablFiltArray,"",100);
    foreach(var element in tabl)
    {
        //выводим атрибуты каждой записи, все доступные атрибуты будут доступны через IntelliSense у элемента
        //в нашем случае это ИД таблицы и ее наименование
        Console.WriteLine(element.Table_ID.ToString()+" * " + element.Table_Caption_Rus);
    }
}

Описанные примеры позволяют удобно работать с WEB-сервисами. В данном случае, не требуется описания дополнительных классов для сериализации и десериализции данных. Атрибуты объектов, методы и классы сервиса доступны через IntelliSense (рис. 7). Это существенно ускоряет и упрощает разработку и читаемость кода.

Рис. 7. Работа с WEB-сервисом в программном коде