Introdução ao Microsoft Windows Workflow Foundation: passo-a-passo do desenvolvedor

Dino Esposito - Solid Quality Learning

Setembro de 2005

Aplica-se a:

  • Microsoft Windows Workflow Foundation

  • Microsoft Windows Vista

Resumo: introduz as tecnologias e os recursos do Microsoft Windows Workflow Foundation que serão de interesse dos desenvolvedores que precisam criar aplicativos orientados por fluxo de trabalho para a plataforma Microsoft .NET. (39 páginas impressas).

Observação: este artigo foi escrito de acordo com a versão beta 1 do Windows Workflow Foundation. Esteja ciente de que é provável que ocorram alterações antes do lançamento definitivo da tecnologia.

Nesta página

Um ponto de partida para a adição do suporte ao fluxo de trabalho à plataforma Windows
Criando o primeiro fluxo de trabalho
Recebendo e consumindo dados
O tempo de execução do fluxo de trabalho
Fluxos de trabalho e atividades
Desenvolvendo uma atividade personalizada
Planejando um fluxo de trabalho mais realista
Conclusão

Um ponto de partida para a adição do suporte ao fluxo de trabalho à plataforma Windows

O Microsoft WWF (Windows Workflow Foundation) é uma estrutura extensível para o desenvolvimento de soluções de fluxo de trabalho na plataforma Windows. Um componente do futuro Microsoft WinFX, o Windows Workflow Foundation fornece a API e as ferramentas para o desenvolvimento e a execução de aplicativos baseados em fluxo de trabalho. O Windows Workflow Foundation oferece um modelo unificado para criar soluções de ponta a ponta que abrangem várias categorias de aplicativos, incluindo fluxos de trabalho humanos e de sistema.

O Windows Workflow Foundation é uma estrutura de fluxo de trabalho ampla e com finalidade geral, projetada desde o início para promover a extensibilidade em todos os níveis. As soluções baseadas no Windows Workflow Foundation são construídas com componentes interconectados que têm o suporte do código do Microsoft .NET e são executados em um aplicativo host. Assim como na criação de suas páginas da Web visualmente em um ambiente feito sob medida, você compõe as etapas de seu fluxo de trabalho específico em um designer visual e adiciona código por trás dos componentes do fluxo de trabalho para implementar regras e definir o processo comercial.

O Windows Workflow Foundation fornece um mecanismo de fluxo de trabalho, uma API gerenciada pelo .NET, serviços de tempo de execução e um designer e depurador visual integrado ao Microsoft Visual Studio 2005. Você pode usar o Windows Workflow Foundation para criar e executar fluxos de trabalho que abranjam o cliente e o servidor e possam ser executados em todos os tipos de aplicativos .NET.

Este artigo fornece uma introdução simples ao Windows Workflow Foundation e mostra como ele funciona por meio de alguns exemplos incrementais.

Um fluxo de trabalho é um modelo de um processo humano ou de sistema que é definido como um mapa de atividades. Uma atividade é uma etapa em um fluxo de trabalho e é a unidade de execução, reutilização e composição de um fluxo de trabalho. O mapa de atividades expressa as regras, as ações, os estados e suas relações. Criado por meio do layout de atividades, um fluxo de trabalho do Windows Workflow Foundation é, em seguida, compilado para um assembly do .NET e executado no tempo de execução do fluxo de trabalho e na CLR (Common Language Runtime).

Criando o primeiro fluxo de trabalho

O Windows Workflow Foundation consiste principalmente em um ambiente ativado pelo .NET que processa objetos especiais criados e implementados a partir de um designer do Visual Studio. O Microsoft .NET Framework 2.0 é necessário a fim de dar suporte ao Windows Workflow Foundation. Um pacote instalador separado adiciona ao Visual Studio 2005 o suporte ao designer e ao modelo de projeto do Windows Workflow Foundation. Após a instalação, um novo nó é adicionado à lista padrão de projetos do Visual Studio 2005, como mostrado na Figura 1.

Aa480214.wwfgetstart_01(pt-br,MSDN.10).png
Figura 1. Modelos de projeto de fluxo de trabalho no Visual Studio 2005

Você pode escolher entre várias opções, cada uma delas identificando um tipo de aplicativo de fluxo de trabalho em particular. A Tabela 1 apresenta uma lista parcial de modelos de projeto de fluxo de trabalho.

Tabela 1. Tipos de projeto de fluxo de trabalho no Visual Studio 2005

Tipo

Descrição

Sequential Workflow Console Application

Cria um projeto para a construção de fluxos de trabalho que contém um fluxo de trabalho seqüencial padrão e um aplicativo host de teste de console.

Sequential Workflow Library

Cria um projeto para a construção de um fluxo de trabalho seqüencial como uma biblioteca.

Workflow Activity Library

Cria um projeto para a construção de uma biblioteca de atividades que podem, posteriormente, ser reutilizadas como blocos estruturais para aplicativos de fluxo de trabalho.

State Machine Console Application

Cria um projeto para a construção de um fluxo de trabalho de máquina de estado e um aplicativo host de console.

State Machine Workflow Library

Cria um projeto para a construção de um fluxo de trabalho de máquina de estado como uma biblioteca.

Empty Workflow

Cria um projeto vazio que pode incluir fluxos de trabalho e atividades.

O Windows Workflow Foundation já inclui o suporte a dois estilos fundamentais de fluxo de trabalho: o fluxo de trabalho seqüencial e o fluxo de trabalho de máquina de estado.

Um fluxo de trabalho seqüencial é ideal para as operações expressas por um pipeline de etapas que são executadas uma depois da outra, até que a última atividade seja concluída. Os fluxos de trabalho seqüenciais, no entanto, não são puramente seqüenciais em sua execução. Eles ainda podem receber eventos externos ou iniciar tarefas em paralelo e, nesse caso, a seqüência exata de execução pode variar um pouco.

Um fluxo de trabalho de máquina de estado é composto por um conjunto de estados, transições e ações. Um estado é denotado como um estado inicial e, depois, com base em um evento, pode ser feita uma transição para outro estado. O fluxo de trabalho de máquina de estado pode ter um estado final que determina o final do fluxo de trabalho.

Vamos supor que você selecione e crie um novo projeto Sequential Workflow Console Application. O Visual Studio 2005 Solution Explorer conterá dois arquivos: workflow1.cs e, inicialmente oculto, workflow1.designer.cs. Esses dois arquivos representam o fluxo de trabalho que está sendo criado. Um fluxo de trabalho do Windows Workflow Foundation consiste no arquivo de modelo de fluxo de trabalho e em uma classe de arquivo de código. A classe workflow1.cs é a classe do arquivo de código na qual você pode escrever sua própria lógica comercial de fluxo de trabalho. A classe workflow1.designer.cs representa a descrição do mapa de atividades. Esse arquivo é gerenciado automaticamente pelo Visual Studio 2005, praticamente da mesma forma que acontece com os formulários em um projeto do Microsoft Windows Forms. À medida que você adiciona atividades ao fluxo de trabalho, o Visual Studio 2005 atualiza a classe do designer com o código do Microsoft C# que cria, por programação, o mapa de atividades. Para dar continuidade à analogia com o Windows Forms, um fluxo de trabalho é como um formulário, ao passo que as atividades são como os controles.

Você pode escolher outra forma de persistência para o layout das atividades — o formato de marcação de fluxo de trabalho XML. Para tentar essa abordagem, exclua o arquivo workflow1.cs do projeto e adicione um novo item de fluxo de trabalho, como mostrado na Figura 2.

Aa480214.wwfgetstart_02(pt-br,MSDN.10).png
Figura 2. Adicionando um item de fluxo de trabalho seqüencial com separação de código

Agora, o seu projeto contém dois arquivos, workflow1.xoml e workflow1.xoml.cs. O primeiro contém a marcação de fluxo de trabalho XML que representa o modelo de fluxo de trabalho; o segundo é uma classe de arquivo de código e contém manipuladores de eventos e código-fonte para o fluxo de trabalho. Se você clicar duas vezes no arquivo .xoml poderá ver o designer visual de fluxo de trabalho em ação (veja a Figura 3).

Não há implicação no tempo de execução em escolher a marcação ou o código para a serialização do modelo de fluxo de trabalho — ambos são equivalentes, uma vez que o fluxo de trabalho é compilado em um assembly.

Os aplicativos de fluxo de trabalho são um misto de atividades que realmente funcionam (por exemplo, enviar ou receber dados) e de atividades compostas, como IfElse e While, que gerenciam a execução de um conjunto de atividades filho. Um fluxo de trabalho pode implementar cenários sofisticados de ponta a ponta, como revisão de documentos, aprovação de OCs, gerenciamento de usuários de TI, intercâmbio de informações entre parceiros, qualquer tipo de assistente ou aplicativos de linha de negócios.

A Figura 3 mostra um fluxo de trabalho de exemplo, extremamente simples, que contém apenas uma atividade — o bloco code1.

Aa480214.wwfgetstart_03(pt-br,MSDN.10).png
Figura 3. O designer de fluxo de trabalho do Visual Studio 2005

O bloco Code corresponde a uma instância da classe Code e representa uma atividade do fluxo de trabalho cujo comportamento é expresso com código definido pelo usuário. O código de back-end é inserido por meio do Visual Studio 2005 simplesmente clicando-se duas vezes no elemento selecionado no designer — o familiar estilo de programação dos aplicativos ASP.NET e de outros projetos do Visual Studio 2005.

Quando você clica duas vezes na atividade, o arquivo de código é aberto, oferecendo um stub para o manipulador de código.

private void code1_ExecuteCode(object sender, EventArgs e)
{
   // Some code here
}

Quaisquer instruções que você digitar no manipulador de código serão executadas quando o tempo de execução do fluxo de trabalho processar o bloco de atividades especificado ao percorrer o fluxo de trabalho. Vamos gerar a saída de uma simples mensagem de boas-vindas.

private void code1_ExecuteCode(object sender, EventArgs e)
{
    Code c = (Code) sender;
    Console.WriteLine("Hello, from '{0}'.\nI'm an instance of the {1} class.", 
       c.ID, c.ToString());
}

Além do layout visual, o fluxo de trabalho consiste no seguinte código salvo no arquivo workflow1.xoml.cs.

using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace HelloWorldWorkflow
{
    public partial class Workflow1 : SequentialWorkflow
    {
        private void code1_ExecuteCode(object sender, EventArgs e)
        {
           Code c = (Code) sender;
           Console.WriteLine("Hello, from '{0}'.\nI'm an instance of the {1} class.", 
                              c.ID, c.ToString());
        }
    }
}

O atributo partial refere-se a classes parciais, um novo conceito do .NET Framework 2.0. Uma classe parcial é uma classe cuja definição pode ser disseminada por arquivos de origem distintos. Cada arquivo de origem parece conter uma definição de classe comum do início ao fim, com exceção de que ela é parcial e não contém toda a lógica exigida pela classe. O compilador mesclará as definições de classes parciais em uma definição completa da classe, que pode ser compilada. As classes parciais não têm nada a ver com a orientação a objetos; elas são uma forma (no nível da origem e limitada por assembly) de estender o comportamento de uma classe dentro de um projeto. No .NET Framework 2.0, as classes parciais são os meios usados para impedir que o Visual Studio 2005 insira código gerado automaticamente nos arquivos de código. Qualquer código de ligação que estiver faltando na classe original será adicionado pelo tempo de execução, por meio da adição de uma classe parcial.

Um fluxo de trabalho só pode ser executado pelo tempo de execução de fluxo de trabalho do Windows Workflow Foundation, e o tempo de execução de fluxo de trabalho requer um aplicativo externo para hospedá-lo, de acordo com algumas regras. Para fins de teste, o Visual Studio 2005 também adiciona um arquivo program.cs ao projeto. O arquivo é um aplicativo simples de console, como o seguinte.

class Program
{
    static AutoResetEvent waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        WorkflowRuntime workflowRuntime = new WorkflowRuntime();
        workflowRuntime.StartRuntime();

        workflowRuntime.WorkflowCompleted += OnWorkflowCompleted;

        Type type = typeof(HelloWorldWorkflow.Workflow1);
        workflowRuntime.StartWorkflow(type);

        waitHandle.WaitOne();
        workflowRuntime.StopRuntime();

        // A bit of feedback to the user    
         Console.WriteLine("");
         Console.WriteLine("");
         Console.WriteLine("==========================");
         Console.WriteLine("Press any key to exit.");
         Console.WriteLine("==========================");
         Console.ReadLine();
    }

    static void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
    {
       waitHandle.Set();
    }
}

Para simplificar, o Visual Studio 2005 embute em código o nome da classe de fluxo de trabalho no aplicativo de console, como você pode ver nas linhas em negrito do código acima. Para impedir que o aplicativo de console seja encerrado imediatamente após a conclusão, convém adicionar uma chamada a Console.ReadLine ao final do método Main. Nesse ponto, você estará pronto para compilar e testar o fluxo de trabalho: pressione F5 e siga em frente. Se tudo correr bem, você deverá ver a saída mostrada na Figura 4.

Aa480214.wwfgetstart_04(pt-br,MSDN.10).png
Figura 4. O fluxo de trabalho de exemplo executado por um aplicativo host de console

Depurar o aplicativo de fluxo de trabalho também é simples. Tudo o que você precisa fazer, na verdade, é colocar um ponto de interrupção. Você pode colocar um ponto de interrupção em qualquer local da classe do arquivo de código do fluxo de trabalho (assim como geralmente faz com código do C#) ou — e isto é realmente interessante — diretamente no modo de exibição do designer. Você seleciona a atividade na qual deseja que o depurador comece e pressiona F9 para definir o ponto de interrupção, como mostrado na Figura 5.

Aa480214.wwfgetstart_05(pt-br,MSDN.10).png
Figura 5. Um ponto de interrupção colocado no modo de exibição do designer do fluxo de trabalho

Assim que o fluxo do código atinge a atividade que tem o ponto de interrupção definido, o Visual Studio 2005 fica sujeito ao depurador de fluxo de trabalho (veja a Figura 6). A partir desse ponto, você pode percorrer o código e as atividades passo a passo no designer visual pressionando F11, como seria de se esperar.

Aa480214.wwfgetstart_06(pt-br,MSDN.10).png
Figura 6. O aplicativo de fluxo de trabalho em uma sessão de depuração

Recebendo e consumindo dados

Vamos continuar e modificar o fluxo de trabalho para fazer com que ele receba e consuma os dados quando sua instância for criada. Existem duas abordagens gerais para receber dados em um fluxo de trabalho no momento da criação da instância: com parâmetros e eventos. Se você optar por parâmetros, definirá manualmente uma lista de nomes e tipos de parâmetros no designer visual. Se você escolher os eventos, precisará criar e adicionar uma atividade personalizada que atue como uma origem externa, seja acionada em algum ponto do modelo de fluxo de trabalho e passe alguns dados. Vamos mostrar a abordagem baseada em eventos mais adiante neste artigo. Por enquanto, nos concentraremos nos parâmetros.

Como mostrado na Figura 7, o painel Properties de Workflow1 mostra uma coleção Parameters, que você preenche com pares de nome/valor no momento do design.

Aa480214.wwfgetstart_07(pt-br,MSDN.10).png
Figura 7. Adicionando parâmetros a um fluxo de trabalho

A Figura 8 mostra o editor de parâmetros em ação. Você cria uma nova entrada para cada parâmetro desejado e indica seu nome, seu tipo e sua direção.

Aa480214.wwfgetstart_08(pt-br,MSDN.10).png
Figura 8. Parâmetros de seqüência de caracteres FirstName e LastName adicionados

O tipo do parâmetro pode ser digitado manualmente ou selecionado a partir de um navegador de objetos personalizado. Ao fechar a caixa de diálogo Workflow Parameters Editor, modifique o arquivo de código para incorporar os parâmetros recém-definidos. Geralmente, você adiciona duas propriedades public e faz com que elas exponham o conteúdo das coleções Parameters, como mostrado no código a seguir.

public partial class Workflow1 : SequentialWorkflow
{
   public string UserFirstName
   {
      get { return (string) Parameters["FirstName"].Value; }
      set { Parameters["FirstName"].Value = value; }
   }

   public string UserLastName
   {
      get { return (string) Parameters["LastName"].Value; }
      set { Parameters["LastName"].Value = value; }
   }   

   :
}

O uso de propriedades public é simplesmente uma boa prática de programação que mantém seu código mais limpo e organizado. Isso não é, de maneira alguma, um requisito para o consumo de dados de parâmetro. A inserção de chamadas diretamente na coleção Parameters do fluxo de trabalho também funcionará. Se você envolver parâmetros em propriedades public, poderá escolher os nomes que desejar para as propriedades. Lembre-se, porém, de que os nomes de parâmetros diferenciam maiúsculas de minúsculas no C#.

Quem fornece, realmente, os dados de entrada por meio desses parâmetros? Quem cuida dessa tarefa é o aplicativo host. É importante observar, porém, que o host define todos os parâmetros no momento da inicialização, quando o fluxo de trabalho é carregado para execução em um recipiente de tempo de execução. Para detalhar um pouco esse ponto, vamos escrever um aplicativo host de exemplo, baseado no Windows Forms. O aplicativo de exemplo fornecerá duas caixas de texto para que os usuários insiram seus nomes e sobrenomes (veja a Figura 9) e as passará para os manipulares de código do fluxo de trabalho. Para consumir os parâmetros, vamos reescrever o manipulador de código da seguinte forma:

private void code1_ExecuteCode(object sender, EventArgs e)
{
    MessageBox.Show("Welcome, " + UserFirstName + " " + UserLastName);
}

O ponto principal do aplicativo host do Windows Forms de exemplo é o manipulador de cliques anexado ao botão Start Workflow.

Aa480214.wwfgetstart_09(pt-br,MSDN.10).png
Figura 9. Um aplicativo host de fluxo de trabalho do Windows Forms

O código-fonte completo da classe code-behind do formulário é o seguinte:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;


namespace WinFormHost
{
    public partial class Form1 : Form
    {
        private WorkflowRuntime _wr = null;
        private string _workflowAssembly = "";
        private string _workflowTypeName = "";

        public Form1()
        {
            InitializeComponent();
            _workflowAssembly = "WorkflowWithParams";
            _workflowTypeName = "WorkflowWithParams.Workflow1";
            _wr = new WorkflowRuntime();
            _wr.StartRuntime();
        }


        private void btnStartWorkflow_Click(object sender, EventArgs e)
        {
            string assemblyName = _workflowAssembly;
            string typeName = _workflowTypeName;

            // Attempt to get type by fully-qualified name
            Assembly assembly = Assembly.Load(assemblyName);
            Type workflowType = assembly.GetType(typeName);

            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("FirstName", txtFirstName.Text);
            parameters.Add("LastName", txtLastName.Text);

            // Start the workflow
            Guid instanceID = Guid.NewGuid();
            _wr.StartWorkflow(workflowType, instanceID, parameters);
        }
    }
}

Para popular a coleção de parâmetros, é preciso usar um dicionário baseado em genéricos, composto por seqüências de caracteres e objetos. O nome do item é uma seqüência de caracteres, ao passo que o valor contido é configurado como um objeto. Você adiciona ao dicionário a mesma quantidade de itens que a dos parâmetros estáticos do modelo de fluxo de trabalho — neste caso, FirstName e LastName. Os dois parâmetros usam o conteúdo digitado nas caixas de texto da interface do usuário.

Finalmente, o fluxo de trabalho é executado quando você cria uma instância do modelo especificado. O método StartWorkflow do objeto de tempo de execução possui várias sobrecargas. A versão usada no código aceita o tipo de fluxo de trabalho, a coleção de parâmetros de entrada e um GUID (identificador global exclusivo) gerado pelo sistema.

Você só precisa de uma instância do tempo de execução do fluxo de trabalho para cada processo, e não é permitido ter mais de uma instância para cada AppDomain. O melhor que você pode fazer aqui é criar a instância necessária diretamente no construtor do formulário. O mesmo objeto de tempo de execução pode controlar uma variedade de instâncias de fluxo de trabalho. O tempo de execução diferencia as instâncias com base em seu GUID e recebe dados particulares para cada instância específica.

Aa480214.wwfgetstart_10(pt-br,MSDN.10).png
Figura 10. O fluxo de trabalho parametrizado em ação, hospedado por um aplicativo do Windows Forms

Para fins puramente educativos, vamos dar uma olhada rápida no código de marcação do designer e do fluxo de trabalho neste estágio do desenvolvimento. Este é o arquivo de origem do workflow1.designer.cs.

public sealed partial class Workflow1 : SequentialWorkflow
{
   private void InitializeComponent()
   {
      ParameterDeclaration FirstName = new ParameterDeclaration();
      ParameterDeclaration LastName = new ParameterDeclaration();
      this.code1 = new System.Workflow.Activities.Code();
      // 
      // code1
      // 
      this.code1.ID = "code1";
      this.code1.ExecuteCode += new System.EventHandler(this.code1_ExecuteCode);
      // 
      // Workflow1
      // 
      this.Activities.Add(this.code1);
      this.DynamicUpdateCondition = null;
      this.ID = "Workflow1";
      FirstName.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
      FirstName.Name = "FirstName";
      FirstName.Type = typeof(string);
      FirstName.Value = null;
      LastName.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
      LastName.Name = "LastName";
      LastName.Type = typeof(string);
      LastName.Value = null;
      this.Parameters.Add(FirstName);
      this.Parameters.Add(LastName);
   }

   private Code code1;
}

Este é o conteúdo de marcação do fluxo de trabalho correspondente.

<?Mapping XmlNamespace="ComponentModel" ClrNamespace="System.Workflow.ComponentModel" 
          Assembly="System.Workflow.ComponentModel" ?>
<?Mapping XmlNamespace="Compiler" ClrNamespace="System.Workflow.ComponentModel.Compiler" 
          Assembly="System.Workflow.ComponentModel" ?>
<?Mapping XmlNamespace="Activities" ClrNamespace="System.Workflow.Activities" 
          Assembly="System.Workflow.Activities" ?>
<?Mapping XmlNamespace="RuleConditions" ClrNamespace="System.Workflow.Activities.Rules" 
          Assembly="System.Workflow.Activities.Rules" ?>
<SequentialWorkflow x:Class="WorkflowWithParams.Workflow1" 
                    x:CompileWith="Workflow1.xoml.cs" 
                    ID="Workflow1" 
                    xmlns:x="Definition" xmlns="Activities">
    <SequentialWorkflow.Parameters>
        <wcm:ParameterDeclaration Name="FirstName" Type="System.String" Direction="In" 
                                  xmlns:wcm="ComponentModel" />
        <wcm:ParameterDeclaration Name="LastName" Type="System.String" Direction="In" 
                                  xmlns:wcm="ComponentModel" />
    </SequentialWorkflow.Parameters>
    <Code ExecuteCode="code1_ExecuteCode" ID="code1" />
</SequentialWorkflow>

Observe que todos os parâmetros definidos de forma estática na coleção Parameters devem ser inicializados explicitamente e passados quando uma instância do fluxo de trabalho é criada para execução.

O tempo de execução do fluxo de trabalho

O host interage com o Windows Workflow Foundation por meio da classe WorkflowRuntime. Não deixe que a aparente simplicidade do host de exemplo mostrado acima o engane em relação a um ponto-chave. Um host pode ser responsabilizado por vários aspectos adicionais e críticos, como a criação de um ou mais processos e um ou mais AppDomains; o empacotamento de chamadas entre os AppDomains, como necessário; e a configuração de mecanismos de isolamento. Um host pode precisar criar vários processos para se beneficiar de várias CPUs em uma máquina, por motivos de escalabilidade, ou para executar um grande número de instâncias de fluxo de trabalho em um farm de máquinas.

Existem outras coisas que um host pode fazer. Por exemplo, ele pode controlar as diretivas a serem aplicadas quando um fluxo de trabalho for sujeitado a uma longa espera, escutar eventos específicos e comunicá-los a um usuário ou administrador, definir tempos limite e repetições para cada fluxo de trabalho, expor contadores de desempenho e gravar informações de log para fins de depuração e diagnóstico.

Os hosts realizam a maioria das tarefas adicionais por meio de serviços predefinidos e personalizados registrados no recipiente no momento da inicialização. O host de exemplo não faz nada disso, e é limitado a iniciar instâncias de fluxo de trabalho. Isso é aceitável em muitas situações comuns.

Fluxos de trabalho e atividades

Vamos voltar e examinar a caixa de ferramentas do Visual Studio 2005 quando um projeto de fluxo de trabalho está ativo. A caixa de ferramentas, mostrada na Figura 11, lista as atividades que você pode usar para criar a seqüência de etapas e suas inter-relações, a fim de formar o modelo de fluxo de trabalho.

Aa480214.wwfgetstart_11(pt-br,MSDN.10).png
Figura 11. Os blocos estruturais de um fluxo de trabalho do Windows Workflow Foundation

A Tabela 2 fornece uma breve descrição de cada atividade, além de alguns cenários nos quais seu uso pode ser útil.

Tabela 2. Blocos estruturais do Windows Workflow Foundation

Atividade

Descrição

Code

Permite adicionar código do Microsoft Visual Basic .NET ou do C# ao fluxo de trabalho para executar ações personalizadas. O código não deve, no entanto, bloquear o fluxo de trabalho com uma dependência de um recurso externo, como um serviço da Web.

Compensate

Permite chamar um código para reverter ou compensar operações já executadas pelo fluxo de trabalho quando ocorre um erro. Normalmente, é aconselhável enviar um e-mail para um usuário que foi notificado anteriormente sobre o sucesso de uma operação que, agora, está sendo cancelada.

ConditionedActivityGroup (CAG)

Permite que seu fluxo de trabalho execute um conjunto de atividades filho de forma condicional, com base em critérios específicos de cada atividade, até que uma condição de conclusão seja atendida para o CAG como um todo. As atividades filho são independentes e podem ser executadas em paralelo.

Delay

Permite controlar o tempo do fluxo de trabalho e insere atrasos nele. Você pode fornecer um tempo limite na atividade Delay para que o fluxo de trabalho faça uma pausa antes de continuar a execução.

EventDriven

Representa uma seqüência de atividades cuja execução é disparada por um evento. A primeira atividade filho deve ser capaz de aguardar por eventos externos. As primeiras atividades filho viáveis são EventSink e Delay. Delay, neste caso, é usada como tempo limite.

EventSink

Permite que o fluxo de trabalho receba dados de um serviço de troca de dados registrado com WorkflowRuntime quando o serviço gerar o evento especificado.

ExceptionHandler

Permite manipular uma exceção de um tipo especificado por você. A atividade ExceptionHandler é um invólucro para outras atividades que irão realmente executar qualquer trabalho necessário quando a exceção especificada ocorrer. Opcionalmente, você pode especificar uma variável local para armazenar a exceção e torná-la disponível no code-behind.

IfElse

Permite que o fluxo de trabalho execute de forma condicional uma das várias ramificações alternativas. Você coloca uma condição em cada ramificação e a primeira ramificação para a qual a condição for verdadeira será executada. Não é preciso colocar uma condição na última ramificação porque ela é tratada como a ramificação "else".

InvokeMethod

Permite que o fluxo de trabalho invoque um método em uma interface para enviar mensagens do fluxo de trabalho para um serviço de troca de dados registrado com WorkflowRuntime.

InvokeWebService

Permite que o fluxo de trabalho invoque um método de serviço da Web. Você especifica a classe de proxy a ser usada (usando WSDL) e o nome do método que deseja invocar. Tanto as chamadas síncronas quanto as assíncronas têm suporte.

InvokeWorkflow

Permite que o fluxo de trabalho chame ou inicie outro fluxo de trabalho, indefinidamente. Por exemplo, um fluxo de trabalho chamado pode chamar um terceiro fluxo de trabalho, que pode chamar um quarto e assim por diante. As chamadas recursivas não têm suporte. O modelo de chamada com suporte é o "dispare e esqueça" (fire-and-forget).

Listen

Permite que o fluxo de trabalho aguarde por um de (potencialmente) vários eventos ou interrompa a espera após um intervalo de tempo limite especificado e seja ramificado com base nos resultados. Você pode adicionar uma ou mais atividades orientadas por eventos a cada ramificação. Apenas a primeira ramificação para a qual uma condição for atendida será seguida; nenhuma das outras ramificações será executada.

Parallel

Permite que o fluxo de trabalho execute duas ou mais operações independentes. A atividade aguarda até que as duas operações sejam encerradas antes de continuar.

Policy

Permite representar e executar uma coleção de regras. Essa atividade não está na caixa de ferramentas; para acessar sua funcionalidade, é preciso criar uma atividade personalizada e usar a derivação.

Replicator

Permite que o fluxo de trabalho crie um número arbitrário de instâncias de uma determinada atividade e as execute em seqüência ou simultaneamente.

SelectData

Permite que o fluxo de trabalho consulte dados externos por meio de um método definido em um objeto de origem de dados externo. Quando a atividade SelectData é disparada, o método associado é executado dentro do thread do host. O valor retornado por esse método é passado para o fluxo de trabalho.

Sequence

Permite coordenar a execução serial de um conjunto de atividades filho. A seqüência é concluída ao final da última atividade filho.

SetState

Permite que o fluxo de trabalho de máquina de estado especifique uma transição para um novo estado.

State

Representa um estado em um fluxo de trabalho de máquina de estado.

StateInitialization

Usada dentro de uma atividade State como um recipiente para as atividades filho que são executadas quando há uma transição para o estado.

Suspend

Suspende a operação do fluxo de trabalho para permitir a intervenção caso haja alguma condição de erro. Quando uma instância do fluxo de trabalho é suspensa, um erro é registrado no log. Você pode especificar uma seqüência de mensagem para ajudar o administrador a diagnosticar o que aconteceu. Todas as informações de estado associadas à instância atual são salvas, e ela é recuperada quando e se o administrador continuar a execução.

Terminate

Permite encerrar imediatamente a operação do fluxo de trabalho caso ocorra alguma situação anormal. Se chamada dentro de uma atividade Parallel, todas as ramificações serão encerradas abruptamente, seja qual for seu estado atual. Quando um fluxo de trabalho é encerrado, um erro é registrado no log e uma mensagem ajuda o administrador a descobrir o que aconteceu.

Throw

Permite acionar uma exceção do tipo especificado. Usar essa atividade equivale a ter um manipulador de código acionando a exceção no código do usuário. A atividade é uma forma declarativa de acionar uma exceção do .NET.

TransactionalContext

Um contexto transacional é um bloco usado para atividades de grupo. Usada principalmente para execução transacional, compensação e manipulação de exceções, essa atividade pode ser também sincronizada. Ao sincronizar um contexto transacional, você garante que qualquer acesso aos dados compartilhados dentro da atividade será serializado adequadamente.

UpdateData

Permite que o fluxo de trabalho atualize um armazenamento de dados por meio de um método definido em um objeto de origem de dados externo. Quando a atividade UpdateData é disparada, o método associado é executado dentro do thread do host.

WaitForData

Permite que o fluxo de trabalho receba informações de um objeto de origem de dados externo. A atividade é disparada quando os dados recebidos modificam o estado da origem de dados ligada. Os dados são recebidos por meio de um serviço de origem de dados ligada.

WaitForQuery

Permite que um aplicativo externo consulte dados em seu fluxo de trabalho. Essa atividade aguarda até que uma consulta do host seja recebida. As consultas de aplicativos externos são enviadas ao fluxo de trabalho usando um método no serviço de origem de dados ligada.

WebServiceReceive

Permite que um fluxo de trabalho que é exposto como um serviço da Web receba, ele mesmo, uma solicitação de serviço da Web.

WebServiceResponse

Permite que um fluxo de trabalho que é exposto como um serviço da Web responda, ele mesmo, a uma solicitação de serviço da Web.

While

Permite que o fluxo de trabalho execute uma ou mais atividades enquanto uma condição estiver sendo atendida. Antes de cada iteração, a condição é avaliada. Se for verdadeira, todas as atividades filho são executadas; caso contrário, a atividade é concluída. Você pode especificar uma condição declarativa ou de código.

As atividades representam a abordagem declarativa à programação de fluxos de trabalho com o Windows Workflow Foundation. Usando atividades, você compõe seu modelo de fluxo de trabalho durante o design e atribui valores às propriedades de cada atividade. O resultado final é salvo como uma marcação XML em um arquivo de marcação de fluxo de trabalho que possui uma extensão .xoml, se você tiver optado por um item de fluxo de trabalho com separação de código. Caso contrário, o modelo composto torna-se persistente em um arquivo de classe do C# ou do Visual Basic .NET gerado pelo designer, como uma seqüência de chamadas para o modelo de objeto do fluxo de trabalho. A primeira abordagem é similar às páginas ASP.NET, ao passo que a segunda é semelhante ao processo dos aplicativos do Windows Forms.

O Visual Studio 2005 oculta a maioria das diferenças entre as duas abordagens. O design do fluxo de trabalho é sempre visual, e o Visual Studio 2005 persiste o seu trabalho de forma transparente em um de dois formatos diferentes. Se você optar por uma solução apenas em código (sem XOML e separação de código), terá a possibilidade de ajustar o código do designer para torná-lo um pouco mais flexível. Por exemplo, você pode fazer com que ele leia valores padrão para os parâmetros a partir de um arquivo de configuração ou de um banco de dados. Se você optar pela marcação de fluxo de trabalho e pela separação de código, terá uma separação organizada do código do fluxo de trabalho e de seu modelo.

É possível modificar o modelo de fluxo de trabalho através de programação? Durante o design, você pode fazer qualquer coisa com um fluxo de trabalho (através de programação) que faria com o Visual Studio. Durante a execução, também é possível fazer atualizações dinâmicas na coleção de atividades, e isso lhe permite fazer alterações em uma instância de um fluxo de trabalho em execução. As alterações dinâmicas são motivadas por alterações comerciais que não eram conhecidas durante o design ou pela necessidade de lógica comercial que modifique e, depois, conclua o processo comercial. De qualquer forma, poucas alterações deverão ser feitas, apenas pequenos ajustes.

As atualizações dinâmicas aplicam uma única instância do fluxo de trabalho no contexto de um aplicativo. As instâncias futuras do mesmo tipo de fluxo de trabalho não serão afetadas pelas alterações. As atualizações dinâmicas em uma instância de fluxo de trabalho podem ser feitas a partir da própria instância e também externamente, a partir do código do aplicativo.

A estrutura do Windows Workflow Foundation oferece suporte à interoperabilidade com serviços da Web, o que inclui a capacidade de expor um fluxo de trabalho como um serviço da Web a clientes ASP.NET e a outros fluxos de trabalho. O Windows Workflow Foundation oferece suporte à publicação de um fluxo de trabalho como um serviço da Web ASP.NET em um servidor Web ou farm de servidores executando o ASP.NET no Microsoft IIS 6.0.

O conjunto de atividades da estrutura do Windows Workflow Foundation contém as atividades WebServiceReceive e WebServiceResponse, que permitem que um fluxo de trabalho seja usado como ponto de extremidade de serviços da Web.

Para ser exposto como um serviço da Web, um fluxo de trabalho deve incluir uma atividade WebServiceReceive para receber chamadas de clientes. Um comando de menu de atalho publica o fluxo de trabalho como um serviço da Web, como mostrado na Figura 12.

Aa480214.wwfgetstart_12(pt-br,MSDN.10).png
Figura 12. Publicação de um fluxo de trabalho como um serviço da Web

Desenvolvendo uma atividade personalizada

O ponto-chave da extensibilidade no Windows Workflow Foundation é a autoria de atividades personalizadas, já que isso permite expandir o conjunto de blocos estruturais que você pode usar para criar modelos de fluxo de trabalho.

Vamos explorar a arquitetura interna de uma atividade desenvolvendo uma atividade personalizada para enviar mensagens de email. O Windows Workflow Foundation fornece um modelo pronto do Visual Studio 2005 para atividades personalizadas. Ele se chama Workflow Activity Library. O modelo cria um arquivo em C# que você pode renomear como quiser — por exemplo, SendMailActivity. Uma atividade é uma classe básica herdeira de uma classe pai. Você pode derivar sua atividade de qualquer outra existente, seja ela uma das atividades internas ou uma criada por você mesmo, ou ainda adquirida de um fornecedor terceiro. Obviamente, a classe pai adiciona um comportamento predefinido ao novo componente. Para criar uma atividade totalmente a partir do zero, faça com que ela seja derivada de Activity. O exemplo de código a seguir mostra o esqueleto da nova classe.

public partial class SendMailActivity : System.Workflow.ComponentModel.Activity
{
   public SendMailActivity()
   {
      InitializeComponent();
   }

   protected override Status Execute(ActivityExecutionContext context)
   {
       : 
   }
}

Como você pode supor, o método Execute é o coração do componente — ou seja, ele é o local onde são executadas as tarefas centrais do componente.

Uma vez desenvolvida, uma atividade é colocada na caixa de ferramentas e está pronta para operações de arrastar e soltar para novos aplicativos de fluxo de trabalho. Embora uma lista de propriedades não seja um requisito, uma atividade sem propriedades não tem grande utilidade. Para adicionar algumas propriedades, selecione a atividade que está sendo desenvolvida no designer e clique na entrada Activity Properties do painel Properties (veja a Figura 13).

Aa480214.wwfgetstart_13(pt-br,MSDN.10).png
Figura 13. Adicionando propriedades a uma atividade personalizada

Adicionar propriedades a uma atividade não é muito diferente de adicionar parâmetros a um fluxo de trabalho. Tudo o que você tem a fazer é configurar um nome e os atributos de cada propriedade desejada. A Figura 14 mostra como adicionar uma propriedade To à atividade SendMail.

Aa480214.wwfgetstart_14(pt-br,MSDN.10).png
Figura 14. A propriedade To adicionada à atividade SendMail

Para concluir, vamos adicionar outras propriedades, como From, Subject, Body e Host, para que os usuários possam configurar completamente os emails enviados. À medida que você adiciona propriedades, o assistente modifica o arquivo code-behind em C# que contém a lógica da atividade.

A etapa final elabora um pouco o método Execute, instruindo-o a enviar um email quando a atividade for executada.

protected override Status Execute(ActivityExecutionContext context)
{
    MailAddress toAddress = new MailAddress(To);
    MailAddress fromAddress = new MailAddress(From);

    MailAddressCollection addresses = new MailAddressCollection();
    addresses.Add(toAddress);

    MailMessage msg = new MailMessage(fromAddress, toAddress);
    msg.Subject = Subject;
    msg.Body = Body;

    SmtpClient mail = new SmtpClient(Host);
    mail.Send(msg);
    return Status.Closed;
}

Se você desenvolver o projeto da atividade dentro de uma solução de fluxo de trabalho, o documento do fluxo de trabalho localizará automaticamente a nova atividade listada na caixa de ferramentas, como mostrado na Figura 15. Caso contrário, você terá que adicioná-la clicando com o botão direito do mouse na caixa de ferramentas.

Aa480214.wwfgetstart_15(pt-br,MSDN.10).png
Figura 15. A atividade SendMail é destacada na caixa de ferramentas

A Figura 16 demonstra que a atividade SendMail realmente funciona.

Aa480214.wwfgetstart_16(pt-br,MSDN.10).png
Figura 16. A atividade SendMail em ação

Planejando um fluxo de trabalho mais realista

Vamos ver como combinar algumas das atividades listadas na Tabela 2 para resolver uma tarefa mais realista. Imagine um aplicativo comercial no qual um pedido pode passar por vários estados antes de ser concluído. Em um cenário típico, existem regras que indicam quais eventos podem ocorrer em um pedido, dependendo de seu estado atual. Por exemplo, um pedido em aberto pode ser processado ou atualizado, mas não cancelado nem embarcado.

Quando ocorre um evento, um fluxo de trabalho de máquina de estado faz a transição de estado do pedido. Por exemplo, quando um pedido está em aberto e o evento BeingProcessed ocorre, o fluxo de trabalho de máquina de estado faz a transição do pedido para o estado apropriado. A Figura 17 mostra o diagrama de um exemplo de fluxo de trabalho de máquina de estado de pedido.

Aa480214.wwfgetstart_17(pt-br,MSDN.10).png
Figura 17. Um esquema de exemplo para uma máquina de estado que gerencia pedidos

Vamos começar criando um fluxo de trabalho de máquina de estado. Você usará a atividade State para modelar os possíveis estados de um pedido. Em seguida, especificará os eventos que podem ocorrer a partir de cada estado, usando atividades EventDriven. Os eventos externos capturados por meio de um serviço personalizado farão a transição do estado do pedido. Para fazer a transição, você usará a atividade SetState. Depois de elaborar o fluxo de trabalho, você o colocará à prova usando um aplicativo host do Windows Forms.

Um fluxo de trabalho se comunica com o mundo exterior por meio de um serviço estabelecido especificamente para essa finalidade. O serviço gera eventos que serão conectados pelas atividades orientadas por eventos, localizadas dentro do fluxo de trabalho. Da mesma forma, o serviço expõe métodos públicos para que o fluxo de trabalho chame e envie dados ao host. Os métodos e os eventos são definidos em uma interface. Essa interface também é conhecida como serviço de troca de dados. Você precisa desse serviço sempre que o fluxo de trabalho interage com componentes externos, tanto na entrada quanto na saída.

Um serviço de troca de dados é uma biblioteca de classes regular do .NET que, no mínimo, inclui uma definição de interface e uma classe que implementa essa interface. A interface é adaptada às tarefas que você deseja representar. Neste caso, uma máquina de estado que representa o ciclo de vida de um pedido, a interface consiste em cinco eventos.

[DataExchangeService]
public interface IOrderService
{
    event EventHandler<OrderEventArgs> OrderCreated;
    event EventHandler<OrderEventArgs> OrderShipped;
    event EventHandler<OrderEventArgs> OrderUpdated;
    event EventHandler<OrderEventArgs> OrderProcessed;
    event EventHandler<OrderEventArgs> OrderCanceled;
}

O atributo [DataExchangeService] marca IOrderService como uma interface do serviço de troca de dados para que o tempo de execução do fluxo de trabalho saiba que será usado para trocar dados com a instância do fluxo de trabalho. Neste caso, o host enviará dados para a instância do fluxo de trabalho gerando eventos para um grupo de atividades EventDriven. Caso seja necessário, a interface IOrderService pode ser chamada de dentro da instância do fluxo de trabalho por meio da atividade InvokeMethod.

A declaração de evento da interface usa genéricos, que são um ótimo novo recurso do .NET Framework 2.0. A classe EventHandler é um delegado que representa o protótipo da função usada para manipular o evento. No .NET Framework 1.x, EventHandler foi definida da seguinte forma.

void EventHandler(object sender, EventArgs e)

Para fazer com que o evento passe uma estrutura de dados personalizada como OrderEventArgs, você deve criar um novo delegado e usá-lo no lugar de EventHandler. Segue um exemplo.

delegate void OrderEventHandler(object sender, OrderEventArgs e)

Esse padrão ainda funciona no .NET Framework 2.0. O advento dos genéricos no .NET Framework 2.0, porém, torna possível obter o mesmo resultado sem definir explicitamente (e criar uma instância de) uma nova classe de delegado. Você usa a versão genérica do delegado EventHandler<T>, na qual o tipo dos dados do evento é um parâmetro.

Os eventos passam dados de cliente do tipo OrderEventArgs, uma classe personalizada que é derivada da classe WorkflowMessageEventArgs do Windows Workflow Foundation, definida no mesmo assembly, como a seguir.

[Serializable]
public class OrderEventArgs : WorkflowMessageEventArgs
{
    private string _orderId;

    public OrderEventArgs(Guid instanceId, string orderId) : base(instanceId)
    {
        _orderId = orderId;
    }

    public string OrderId
    {
       get { return _orderId; }
       set { _orderId = value; }
    }
}

Na próxima etapa, você define uma classe que implementa a interface. A classe terá tantos métodos públicos quantos forem os eventos na interface a serem gerados.

public class OrderService : IOrderService
{
    public OrderService()
    {
    }

    public void RaiseOrderCreatedEvent(string orderId, Guid instanceId)
    {
        if (OrderCreated != null)
            OrderCreated(null, new OrderEventArgs(instanceId, orderId));
    }

    public void RaiseOrderShippedEvent(string orderId, Guid instanceId)
    {
        if (OrderShipped != null)
            OrderShipped(null, new OrderEventArgs(instanceId, orderId));
    }

    public void RaiseOrderUpdatedEvent(string orderId, Guid instanceId)
    {
        if (OrderUpdated != null)
            OrderUpdated(null, new OrderEventArgs(instanceId, orderId));
    }

    public void RaiseOrderProcessedEvent(string orderId, Guid instanceId)
    {
        if (OrderProcessed != null)
            OrderProcessed(null, new OrderEventArgs(instanceId, orderId));
    }
        
    public void RaiseOrderCanceledEvent(string orderId, Guid instanceId)
    {
        if (OrderCanceled != null)
            OrderCanceled(null, new OrderEventArgs(instanceId, orderId));
    }

    public event EventHandler<OrderEventArgs> OrderCreated;
    public event EventHandler<OrderEventArgs> OrderShipped;
    public event EventHandler<OrderEventArgs> OrderUpdated;
    public event EventHandler<OrderEventArgs> OrderProcessed;
    public event EventHandler<OrderEventArgs> OrderCanceled;
}

Agora, você compila o assembly com o serviço de pedidos e volta para o projeto do fluxo de trabalho de máquina de estado. No projeto do fluxo de trabalho, você primeiro adiciona uma referência ao assembly que acabou de criar. Em seguida, adiciona quatro atividades State e as nomeia da seguinte forma: WaitingForOrderState, OrderOpenState, OrderProcessedState, OrderCompletedState.

A Tabela 3 representa o diagrama de estado do fluxo de trabalho. Cada estado possui alguns eventos capazes de causar uma transição para outro estado.

Tabela 3. Um exemplo de máquina de estado para pedidos

Estado

Eventos com suporte

Transição para

WaitingForOrderState

OrderCreated

OrderOpenState

OrderOpenState

OrderUpdated

OrderProcessed

OrderOpenState

OrderProcessedState

OrderProcessedState

OrderUpdated

OrderCanceled

OrderShipped

OrderOpenState

Atividade Terminate

OrderCompletedState

OrderCompletedState

 

 

Para implementar o diagrama, você adiciona cada atividade State como tantos blocos EventDriven quantos forem os eventos suportados da tabela. Por exemplo, a atividade State chamada WaitingForOrderState conterá uma única atividade EventDriven chamada, por exemplo, OrderCreatedEvent (o nome é arbitrário). Como mostrado na Figura 18, a atividade EventDriven incorpora uma atividade EventSink e uma atividade SetState para capturar o evento externo e fazer a transição para um novo estado.

Aa480214.wwfgetstart_18(pt-br,MSDN.10).png
Figura 18. Uma visão interna da atividade EventDriven de OrderCreatedEvent

No painel Properties da atividade EventSink, você seleciona o serviço de troca de dados desejado — a interface IOrderService, neste caso — e o nome do evento para inscrição. Se você clicar na entrada InterfaceType do painel Properties da atividade EventSink, o Visual Studio 2005 fornecerá uma lista dos serviços de troca de dados disponíveis para o projeto. Uma vez selecionado o serviço, a propriedade EventName refletirá a lista de eventos expostos pelo serviço. Você seleciona o evento de seu interesse e segue em frente. Para a atividade OrderCreatedEvent, selecione o evento OrderCreated.

A atividade SetState faz a transição da máquina para o novo estado indicado por sua propriedade TargetState. A atividade SetState da Figura 18 está definida para OrderOpenState.

Você repete a operação acima para todos os estados e coletores de eventos da Tabela 3 e, no final, seu fluxo de trabalho deverá se parecer com o da Figura 19.

Aa480214.wwfgetstart_19(pt-br,MSDN.10).png
Figura 19. A máquina de estado de pedidos finalizada

A etapa final envolve a criação de um aplicativo do Windows Forms para testar o fluxo de trabalho. A interface do usuário inclui um modo de exibição de lista para controlar todos os pedidos pendentes e uma caixa de texto e um botão para criar novos pedidos. Outros botões serão usados para atualizar, processar e encerrar o pedido.

O fluxo de trabalho de máquina de estado é inicializado no evento Form_Load. A inicialização de um fluxo de trabalho de máquina de estado é um pouco mais complexa que a de um fluxo de trabalho seqüencial, principalmente se você quiser ser capaz de controlar alterações de estado. O exemplo de código a seguir mostra como inicializar o tempo de execução do fluxo de trabalho.

private void StartWorkflowRuntime()
{
   // Create a new Workflow Runtime for this application
   _runtime = new WorkflowRuntime();

   // Register event handlers for the WorkflowRuntime object
   _runtime.WorkflowTerminated += new 
          EventHandler<WorkflowTerminatedEventArgs>(WorkflowRuntime_WorkflowTerminated);
   _runtime.WorkflowCompleted += new 
          EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);

    // Create a new instance of the StateMachineTrackingService class  
    _stateMachineTrackingService = new StateMachineTrackingService(_runtime);

    // Start the workflow runtime 
    _runtime.StartRuntime();

    // Add a new instance of the OrderService to the runtime
    _orderService = new OrderService();
    _runtime.AddService(_orderService);
}

O StateMachineTrackingService funciona sobre o tempo de execução e o estende com a capacidade de controlar alterações de estado no fluxo de trabalho. Uma instância do serviço de troca de dados também é adicionada ao tempo de execução.

Quando os usuários clicam para criar um novo pedido, o seguinte código é executado.

private Guid StartOrderWorkflow(string orderID)
{
   // Create a new GUID for the WorkflowInstanceId
   Guid instanceID = Guid.NewGuid();

   // Load the OrderWorkflows assembly
   Assembly asm = Assembly.Load("OrderWorkflows");

   // Get a type reference to the OrderWorkflows.Workflow1 class
   Type workflowType = asm.GetType("OrderWorkflows.Workflow1");

   // Start a new instance of the state machine with state tracking support
   StateMachineInstance stateMachine = 
          _stateMachineTrackingService.RegisterInstance(workflowType, instanceID);
   stateMachine.StateChanged += new 
          EventHandler<ActivityEventArgs>(StateMachine_StateChanged);
   stateMachine.StartWorkflow();
   _stateMachineInstances.Add(instanceID.ToString(), stateMachine);

   // Return the workflow GUID 
   return instanceID;
}

Primeiro, o código cria a instância do fluxo de trabalho e registra um manipulador de eventos para as alterações de estado. Observe que o uso da Reflexão do .NET para a obtenção das informações de tipo não é estritamente necessário, mas adiciona muita flexibilidade. O velho e simples operador typeof também funcionaria bem para comunicar o tipo de instância de fluxo de trabalho ao tempo de execução do fluxo de trabalho.

A Figura 20 mostra o aplicativo de exemplo em ação. Os botões são habilitados com base no estado da instância de fluxo de trabalho selecionada.

Aa480214.wwfgetstart_20(pt-br,MSDN.10).png
Figura 20. O fluxo de trabalho de máquina de estado hospedado em um aplicativo do Windows Forms

Quando o usuário clica em um determinado botão, o evento correspondente na interface de comunicação é gerado e capturado pelo coletor de eventos do fluxo de trabalho. Por exemplo, um clique no botão Order Processed de uma instância do fluxo de trabalho no estado em aberto é manipulado da seguinte forma.

private void btnOrderEvent_Click(object sender, EventArgs e)
{
   // Get the name of the clicked button 
   string buttonName = ((Button)sender).Name;

   // Get the GUID of the selected order
   Guid instanceID = GetSelectedWorkflowInstanceID();

   // Get the ID of the selected order
   string orderID = GetSelectedOrderID();

   // Disable buttons before proceeding
   DisableButtons();

   // Determines what to do based on the name of the clicked button
   switch(buttonName)
   {
      // Raise an OrderShipped event using the Order Local Service
      case "btnOrderShipped":
         _orderService.RaiseOrderShippedEvent(orderID, instanceID);
     break;

      // Raise an OrderUpdated event using the Order Local Service
      case "btnOrderUpdated":
         _orderService.RaiseOrderUpdatedEvent(orderID, instanceID);
         break;

      // Raise an OrderCanceled event using the Order Local Service
      case "btnOrderCanceled":
         _orderService.RaiseOrderCanceledEvent(orderID, instanceID);
         break;

      // Raise an OrderProcessed event using the Order Local Service
      case "btnOrderProcessed":
         _orderService.RaiseOrderProcessedEvent(orderID, instanceID);
         break;
     }
}

O evento gerado no fluxo de trabalho é capturado pela atividade EventDriven da Figura 21.

Aa480214.wwfgetstart_21(pt-br,MSDN.10).png
Figura 21. O bloco EventDriven da máquina de estado para manipular o evento Order Processed

A atividade EventSink captura o evento e o processa fazendo a transição para o estado definido pela atividade SetState. A alteração de estado no fluxo de trabalho é detectada pelo serviço adicional de controle de estado e reportada ao host por meio do evento StateChanged, como mostrado nas listagens anteriores.

Você encontrará o código-fonte completo de todos os exemplos discutidos neste documento e muito mais conteúdo para fluxos de trabalho em https://msdn.microsoft.com/workflow.

Conclusão

Projetado para se tornar a estrutura de fluxo de trabalho para produtos da Microsoft novos e já existentes, o Windows Workflow Foundation fornece a potência do WinFX e a facilidade de uso do Visual Studio 2005 para todos os desenvolvedores que precisem criar aplicativos orientados por fluxo de trabalho para a plataforma .NET.

O principal benefício do Windows Workflow Foundation é um modelo de fluxo de trabalho unificado e um conjunto de ferramentas que substitui muitas bibliotecas proprietárias. Sob este aspecto, o Windows Workflow Foundation também é de grande importância para os fornecedores dos atuais produtos de fluxo de trabalho, já que sua adoção significa que eles não precisarão mais manter seu código de nível baixo e poderão se concentrar em tarefas de nível mais alto.

O Windows Workflow Foundation é uma tecnologia de fluxo de trabalho projetada para atender a mais de um tipo específico de aplicativo e necessidades. Ele é uma estrutura ampla, arquitetada para fornecer extensibilidade em todos os níveis. Os melhores exemplos dessa forma de extensibilidade são as atividades personalizadas e os serviços de tempo de execução que podem ser conectados. As atividades personalizadas permitem estender o conjunto de blocos estruturais que você pode usar para criar fluxos de trabalho. Os serviços de tempo de execução, como a persistência e o controle, podem ser alterados de acordo com o ambiente do aplicativo e para criar a persistência no Microsoft SQL Server ou em bancos de dados de outros fornecedores.

As extensões do Visual Studio 2005 para o Windows Workflow Foundation permitirão a modelagem visual de fluxos de trabalho e também o acesso direto ao código.

O designer visual também pode ser hospedado em outros ambientes de design, permitindo que os provedores de designers incorporem o recurso de modelagem visual a seus próprios ambientes e forneçam uma experiência familiar aos usuários do aplicativo.

Este artigo apenas cobriu superficialmente as tecnologias e os recursos do Windows Workflow Foundation, apresentando uma visão geral de seu funcionamento, de sua parte interna e do código de exemplo mais importante.

Sobre o autor

Dino Esposito é um dos mentores da Solid Quality Learning e é o autor de "Programming Microsoft ASP.NET 2.0" (Microsoft Press, 2005). Residente na Itália, Dino é um orador sempre presente em eventos da indústria no mundo inteiro. Entre em contato pelo endereço cutting@microsoft.com ou acesse o blog, em https://weblogs.asp.net/despos.

© .