Command
Шаблон кастомной команды
Шаблон описания кастомной команды в серверном xml-файле:
В качестве значения атрибута Assembly
указывается имя кастомной сборки, в которой реализована команда. А в атрибуте Type
указывается имя класса, описывающего команду.
На сервере команды разделены на две части: инициируемая часть и исполняемая часть. Инициируемая часть представляется классом AbstractCommand, а исполняемая часть - AbstractCommandExecutor. Инициируемая часть используется для парсинга xml-кода, а исполняемая часть - для выполнения бизнес-логики.
Инициируемая часть
Шаблон кода инициируемой части:
Метод Init() принимает параметр XmlNode node, который соответствует тэгу <Command>
, описанному в xml-файле формы. Из объекта node можем получить информацию обо всех вложенных тэгах и их атрибутах. Значения, полученные из xml, сохраняются в публичные свойства, доступ к которым будет у исполняемой части через прямую ссылку на экземпляр класса CustomCommand, переданную в конструктор класса CustomCommandExecutor.
Исполняемая часть
Пример кода исполняемой части:
Метод Execute принимает параметр parameters типа IParameterized, хранящий контекст параметров, общий для всех команд. Из контекста можно получить словарь параметров, который можно использовать для подстановки значений в SQL-запросы вместо переменных.
Как это работает
При старте сервер разбирает серверный xml-файл и создает экземпляр класса CustomCommand для каждого тэга <Command>
типа CustomCommand. Когда происходит вызов команды, первым делом выполняется метод CreateExecutor из класса CustomCommand, который создает экземпляр класса CustomCommandExecutor.
Элементы команды
SQL-запросы
Пример команды с указанием текста SQL-запроса:
Инициируемая часть
Для получения значения обязательного тэга в методе Init необходимо использовать статический метод GetRequiredElementValue класса XmlParser:
Вторым параметром (string path) указывается полный путь до тэга, значение которого нужно получить. В третьем параметре (string name) указывается имя объекта, в котором происходит получение значения, в четвертом параметре (object targetObject) - класс объекта, в котором происходит получение значения. Имя объекта и его класс используются в формировании текста сообщения об ошибке. В качестве результата метод возвращает строку. Если элемент отсутствует, будет возвращено исключение типа InvalidXmlException.
Если необходимо получить значение необязательного элемента, то следует использовать метод GetElementValue. Этот метод в случае отсутствия элемента будет возвращать значение по умолчанию, переданное в параметрах метода.
Исполняемая часть
Если команда должна работать с SQL-запросами, то в конструкторе CustomCommandExecutor необходимо получить ссылки на сервисы обеспечивающие работу с базой данных. Для этого используется статический метод GetRequiredService класса ServiceProvider.
Первый сервис - объект типа IDatabaseConnection, который устанавливает соединение с базой данных и выполняет SQL-запрос:
Второй сервис - объект типа ISqlQueryHelper, который помогает построить текст запроса, заменив в нем переменные в круглых скобках {}
на нужные значения из словаря параметров:
Для выполнения SQL-запросов в методе Execute используем метод ExecuteQueryAsync() у объекта типа IDatabaseConnection, результатом которого будет объект типа DataTable:
В метод передаются текст SQL-запроса и словарь параметров parameterDictionary. Словарь должен содержать значения исходного словаря parameters.ToDictionary() и дополнительные, необходимы для выполнения запроса.
Внутри метод ExecuteQueryAsync() первым делом заменит все переменные в тексте запроса, а затем, с помощью параметров из parameters.ToDictionary(), построит тексты системных запросов с установкой параметров конфигурации.
При необходимости можно в своем коде сделать замену переменных в тексте запроса, используя сервис ISqlQueryHelper и его метод GetQueryText():
Команды
Пример описания кастомной команды, которая принимает имя другой команды, чтобы использовать ее результат выполнения или вызвать ее для выполнения, передав в нее свои значения:
В примере рассматривается команда AnySqlQueryCommand типа SqlQueryCommand, но это может быть команда любого типа, в том числе и кастомного. Вызов команды AnySqlQueryCommand можно реализовать в xml-файле перед вызовом команды MyCustomCommand, либо перенести в исходный код кастомки.
Инициируемая часть
Для получения значения обязательного атрибута тэга в методе Init необходимо использовать статический метод GetRequiredAttributeValue класса XmlParser:
Вторым параметром (string path) указывается полный путь до тэга, значение атрибута которого нужно получить. Имя атрибута указывается в третьем параметре (string attribute). В данном случае используется константа NAME_ATTRIBUTE
, описанная в базовом классе. Если элемент или его атрибут отсутствует, будет возвращено исключение типа InvalidXmlException.
Если необходимо получить значение необязательного атрибута, то следует использовать метод GetAttributeValue. Этот метод в случае отсутствия элемента или его атрибута будет возвращать значение по умолчанию, переданное в параметрах метода.
Исполняемая часть
При работе с командой первым делом нужно получить ее Executor:
Через свойство WorkflowType нашей команды получаем текущий процесс, у которого с помощью метода GetCommandExecutor получаем Executor нужной команды по ее имени.
Результат выполнения команды храниться в свойстве Value:
Так как в примере команда AnyCommandName имеет тип SqlQueryCommand, то ее результат можно привести к типу DataTable, что облегчит его обработку.
Но, прежде чем использовать результат выполнения команды, проверяем значение свойства Value. И если там пусто, то выполняем команду, вызвав метод Execute и передав в него параметры:
Логирование
Для возможности добавления записей в журнал событий Windows необходимо получить сервис ILogger, для этого в конструкторе CustomCommandExecutor используем статический метод GetService класса ServiceProvider.
Чтобы записать в журнал событий сообщение об ошибке, необходимо вызвать метод LogError:
Есть разные уровни сообщений для записи в журнал событий:
LogCritical() - форматирует и записывает критическое сообщение журнала;
LogDebug() - форматирует и записывает в журнал сообщение отладки;
LogError() - форматирует и записывает в журнал сообщение об ошибке;
LogInformation() - форматирует и записывает в журнал информационное сообщение;
LogTrace() - форматирует и записывает в журнал сообщение трассировки;
LogWarning() - форматирует и записывает в журнал сообщение с предупреждением.
Last updated