Обзор рабочего потока Windows Workflow. Часть 6

Источник: rsdn

Читать часть 5

Пользовательские службы

В дополнение к встроенным службам, таким как служба постоянства и служба отслеживания, вы также можете добавлять свои собственные объекты в коллекцию служб, поддерживаемую WorkflowRuntime. Эти службы обычно определяются с применением интерфейса и реализации, так что вы можете легко заменить одну службу другой, не перекодируя рабочий поток.

Ранее представленный в этой главе конечный автомат использует следующий интерфейс:

[ExternalDataExchange]
public interface IDoorService
{
   void LockDoor();
   void UnlockDoor();
   event EventHandler<ExternalDataEventArgs> RequestEntry;
   event EventHandler<ExternalDataEventArgs> OpenDoor;
   event EventHandler<ExternalDataEventArgs> CloseDoor;
   event EventHandler<ExternalDataEventArgs> FireAlarm;
   void OnRequestEntry(Guid id);
   void OnOpenDoor(Guid id);
   void OnCloseDoor(Guid id);
   void OnFireAlarm();
}

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

Внутри конечного автомата присутствует ряд экземпляров CallExternalMethodActivity, используемых для вызова методов этого внешнего интерфейса. Один пример - когда дверь запирается и отпирается. Рабочий поток при этом должен вызвать метод UnlockDoor или LockDoor, и служба реагирует на это посылкой соответствующей команды дверному замку.

Когда служба нуждается во взаимодействии с рабочим потоком, то делает это через события; исполняющая среда рабочих потоков также включает службу по имени ExternalDataExchangeService, которая служит прокси для таких событий. Этот прокси используется при возбуждении события, но поскольку рабочий поток может и не быть загруженным в память на момент доставки события, это событие сначала маршрутизируется внешней службе обмена, которая проверяет, загружен ли рабочий поток, и если нет, то восстанавливает его из постоянного хранилища и затем передает событие в этот рабочий поток.

Код, применяемый для конструирования ExternalDataExchangeService и прокси для событий, определенных в службе, показан ниже:

WorkflowRuntime runtime = new WorkflowRuntime();
ExternalDataExchangeService edes = new ExternalDataExchangeService();
runtime.AddService(edes);
DoorService service = new DoorService();
edes.AddService(service);

Это конструирует экземпляр внешней службы обмена, добавляет его в исполняющую среду, затем создает экземпляр DoorService (реализующий IDoorService) и добавляет его во внешнюю службу обмена данными.

Метод ExternalDataExchangeService.Add конструирует прокси для каждого события, определенного пользовательской службой, так что сохраненный рабочий поток может быть загружен до того, как будет доставлено событие. Если вы не помещаете службу внутрь внешней службы обмена данными, то когда вы станете инициировать события, никто не будет их прослушивать, поэтому они не будут доставлены соответствующему рабочему потоку.

События используют класс ExternalDataEventArgs, поскольку он включает идентификатор экземпляра рабочего потока, которому должно быть доставлено событие. Если существуют и другие значения, которые нужно передать из внешнего события в рабочий поток, то вы должны унаследовать класс от ExternalDataEventArgs и добавить требуемые значения в виде свойств в этот класс.

Хостинг рабочих потоков

Код для хостинга WorkflowRuntime в процессе будет варьироваться в зависимости от самого приложения.

Для приложений Windows Forms или Windows Service (служб Windows), типичным является конструирование исполняющей среды при запуске приложения и сохранение ее в качестве свойства главного класса приложения.

В ответ на некоторый ввод в приложении (например, щелчок пользователя на кнопке пользовательского интерфейса) вы можете затем сконструировать экземпляр рабочего потока и запустить этот экземпляр локально. Рабочему потоку может понадобиться взаимодействие с пользователем, поэтому, например, вы можете определить внешнюю службу, которая запросит пользовательского подтверждения, прежде чем отправит запрос серверу заднего плана.

При хостинге рабочих потоков в ASP.NET обычно вы не будете предупреждать пользователя через окно сообщения, а вместо этого выполните переход на другую страницу сайта, которая сначала затребует подтверждения и затем предоставит соответствующую страницу подтверждения. При развертывании исполняющей среды внутри ASP.NET принято переопределять событие Application_Start и конструировать в нем экземпляр исполняющей среды рабочего потока, который будет доступен всем прочим частям сайта. Вы можете сохранить экземпляр исполняющей среды в статическом свойстве, но лучше сохранить его в состоянии приложения и предусмотреть метод для доступа, который будет извлекать исполняющую среду из состояния приложения, чтобы ее можно было использовать в любом месте приложения.

При любом сценарии - Windows Forms или ASP.NET - вы будете конструировать экземпляр исполняющей среды рабочего потока и добавлять службы так, как показано ниже.

WorkflowRuntime workflowRuntime = new WorkflowRuntime();
workflowRuntime.AddService(
   new SqlWorkflowPersistenceService(conn, true, new TimeSpan(1,0,0),
   new TimeSpan(0,10,0)));
// Здесь выполнить рабочий поток...

Для выполнения рабочего потока понадобится создать экземпляр этого рабочего потока, вызвав для этого метод CreateInstance исполняющей среды. У этого метода есть ряд перегрузок, которые можно применять для конструирования экземпляра рабочего потока на основе кода либо рабочего потока, определенного с помощью XML.

Вплоть до этого места настоящей главы вы рассматривали рабочие потоки как классы .NET - и в самом деле это одно представление рабочего потока. Однако вы можете определить рабочий поток, используя для этого XML, а исполняющая среда создаст его представление в памяти и затем выполнит его, когда вы вызовете метод Start из WorkflowInstance.

Внутри Visual Studio вы можете создать рабочий поток на базе XML, выбрав элемент Sequential Workflow (with code separation) (Последовательный рабочий поток (с разделением кода)) или State Machine Workflow (with code separation) (Рабочий поток в виде конечного автомата (с разделением кода)) в диалоговом окне Add New Item (Добавить новый элемент). Это создаст XML-файл с расширением .xoml и загрузит его в конструктор.

По мере добавления действий в конструкторе они будут отражаться в XML, и структура элементов определит отношения "родительский-дочерний" между действиями. Приведенный ниже XML демонстрирует простой последовательный рабочий поток, содержащий IfElseAcvtivity и два кодовых действия, по одному на каждую ветвь IfElseAcvtivity.

<SequentialWorkflowActivity x:Class="DoorsWorkflow.Workflow1" x:
   Name="Workflow1"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
   <IfElseActivity x:Name="ifElseActivity1">
      <IfElseBranchActivity x:Name="ifElseBranchActivity1">
         <IfElseBranchActivity.Condition>
            <CodeCondition Condition="Test" />
         </IfElseBranchActivity.Condition>
         <CodeActivity x:Name="codeActivity1" ExecuteCode="DoSomething" />
      </IfElseBranchActivity>
      <IfElseBranchActivity x:Name="ifElseBranchActivity2">
         <CodeActivity x:Name="codeActivity2" ExecuteCode="DoSomethingElse" />
      </IfElseBranchActivity>
   </IfElseActivity>
</SequentialWorkflowActivity>

Свойства, определенные в действиях, сохраняются в XML в виде атрибутов, а каждое действие сохраняется как элемент. В XML несложно заметить, что отношение между родительскими действиями (такими как SequentialWorkflowActivity и IfElseActivity) и дочерними действиями определяется структурой.

Выполнение рабочего потока на основе XML никак не отличается от выполнения рабочего потока, описанного в коде - вы просто используете перегрузку метода CreateWorkflow, принимающего экземпляр XmlReader, и затем запускаете этот экземпляр, вызывая метод Start.

Одно преимущество рабочих потоков на основе XML перед рабочими потоками на основе кода заключается в том, что вы легко можете сохранить его определение в базе данных. Вы можете загрузить этот XML во время выполнения и запустить рабочий поток; кроме того, очень легко вносить изменения в определение рабочего потока без необходимости перекомпиляции какого-либо кода.

Изменение рабочего потока во время выполнения поддерживается независимо от того, определен ли он в XML или в коде. Вы просто конструируете объект WorkflowChanges, содержащий все новые действия, которые нужно добавить к рабочему потоку, и затем вызываете метод ApplyWorkflowChanges, определенный в классе WorkflowInstance, для фиксации этих изменений. Это исключительно удобно, поскольку бизнес-правила часто меняются и, к примеру, вам может понадобиться применить изменения к рабочему потоку политики страхования, чтобы клиенту отправлялось электронное письмо с соответствующим уведомлением за месяц до наступления даты изменений. Изменения вносятся на уровне экземпляра, поэтому если у вас есть 100 таких рабочих потоков политики в системе, вам придется внести изменения в каждый из них.

Конструктор Workflow Designer

И в завершение этой главы рассмотрим самое интересное. Конструктор Workflow Designer, который вы используете для проектирования рабочих потоков, не привязан к Visual Studio. При необходимости вы можете развернуть его в среде своего собственного приложения.

Это означает, что вы можете поставлять систему, включающую рабочие потоки, и позволить пользователям настраивать ее под свои нужды, без необходимости приобретения копии Visual Studio. Развертывание конструктора, однако, довольно-таки сложная задача, и ей можно было бы посвятить несколько глав. В Internet можно найти немало примеров развертывания этого конструктора, кроме того, рекомендуется почитать статью MSDN на данную тему, которая доступна по адресу http://msdn2.microsoft.com/en-us/library/aa480213.aspx.

Традиционный способ позволить пользователям настраивать систему заключается в определении интерфейса и затем предоставлении им самостоятельно его реализовывать для расширения функциональности.

В Windows Workflow это расширение становится в значительной степени графическим, поскольку вы можете предоставить пользователю пустой рабочий поток в качестве шаблона и обеспечить панелью инструментов, включающей специальные действия, которые отвечают нуждам вашего приложения. Затем пользователи самостоятельно смогут конструировать свои рабочие потоки, добавляя ваши действия или разрабатывая собственные.

Резюме

Windows Workflow приведет к радикальным изменениям в способе конструирования приложений. Вы можете теперь сформировать сложные части приложения в виде действий и позволить пользователям изменять работу системы простым перетаскиванием действий в рабочий поток.

Почти не существует приложений, к которым нельзя было бы применить концепцию рабочего потока - от простейших утилит командной строки до наиболее сложных систем, состоящих из многих сотен модулей. В то время как новые средства WCF и средства пользовательского интерфейса WPF являются значительным шагом вперед для приложений в целом, добавление Windows Workflow приведет к фундаментальным изменениям в способах разработки и конфигурирования приложений.

Если у вас есть время, чтобы потратить его на одно из новых средств .NET Framework 3.0, настоятельно рекомендуем обратиться именно к Windows Workflow. Есть мнения, что за несколько лет потребность в специалистах по рабочим потокам значительно вырастет.

Окончание.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=23995