Windows Workflow Foundation (WF) – Parte 1 – Coded Workflows

Publicado em 14/01/2012

14


Olá pessoal. Tudo certo?!

Windows workflow foundation permite o desenvolvimento de aplicações poderosas e altamente customizáveis.

A idéia fundamental é permitir a definição e execução de fluxos de atividades. Esses fluxos podem ter seu estado de execução persistidos e recuperados facilmente. Ou seja, os fluxos de atividades podem ter curta ou longa duração. Isso faz com que sejam opções interessantes quando temos a necessidade de orquestrar processos.

A definição de fluxos pode ocorrer de diversas formas. Entre elas, usando:

  • um editor gráfico (presente no Visual Studio e que pode ser “embutido” em nossas aplicações);
  • arquivos de configuração  XML (XAML para ser mais preciso);
  • diretamente no código.

No post de hoje – o primeiro de uma série – mostro os fundamentos para definição de workflows no código. Para mim, essa é a forma mais rápida (não a mais fácil) para definir fluxos.

Começando..

Workflows podem ser definidos facilmente em código. Para que isso seja possível, em qualquer tipo de projeto, basta adicionar uma referência para System.Activities. Para esse post, começo mostrando fluxos definidos em uma aplicação console. Observe:

using System;
using System.Activities;
using System.Activities.Statements;

namespace CodedWorkflows
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Workflow demo";
            new Program().Run();
            Console.WriteLine("ENTER to exit...");
            Console.ReadLine();
        }

        private void Run()
        {
            WorkflowInvoker.Invoke(CreateWorkflow());
        }

        private Activity CreateWorkflow()
        {
            return new Sequence();
        }
    }
}

O que podemos observear nesse código?!

  1. A execução de um fluxo é disparada, normalmente, pelo método estático Invoke da classe WorkflowInvoker;
  2. Um fluxo pode ser reduzido a uma única atividade (no nosso exemplo, uma pré-definida chamada Sequence).

Agora, vejamos uma definição de fluxo mais “interessante”:

private Activity CreateWorkflow1()
{
    return new Sequence()
    {
        DisplayName = "Main Sequence",
        Activities = {
            new WriteLine()
            {
                DisplayName="HelloWorld",
                Text="Hello, World from WF"
            },
            new WriteLine()
            {
                DisplayName="DisplayTime",
                Text=string.Format("The time is: {0}", DateTime.Now)
            }
        }
    };
}

Repare como podemos fazer uso de functional construction. Parecido com o que fazemos quando usamos LINQ to Xml.

Como pode perceber,

  • cada atividade possui uma propriedade DisplayName. Ela é importante para identificar a etapa quando o ffluxo estiver em execução;
  • o framework disponibiliza um conjunto rico de atividades pré-definidas (entre elas WriteLine), para que possamos utilizar em nossos fluxos.

Executando o fluxo, temos:

image

Perfeito.

Variáveis do fluxo

Como os fluxos podem ser persistidos durante a execução para, depois, serem recuperados e concluídos, é importante que exista uma forma especial para manipulação do estado. Observe:

private Activity CreateWorkflow2()
{
    var currentHour = new Variable<int>("hour", DateTime.Now.Hour);
    return new Sequence()
    {
        Variables = {
            currentHour,
        },
        DisplayName = "Main Sequence",
        Activities = {
            new WriteLine()
            {
                DisplayName="HelloWorld",
                Text="Hello, World from WF"
            },
            new WriteLine()
            {
                DisplayName="DisplayHour",
                Text= new InArgument<string>(ctx => currentHour.Get(ctx).ToString())
            }
        }
    };
}

Como indicado no código:

  • WF define um tipo especial para designar as variáveis do fluxo;
  • Variáveis podem conter, em sua definição, um nome e um valor default;
  • Variáveis são “plugadas” no fluxo;
  • Valores das variáveis são recuperados dentro de cada etapa como um argumento. Variáveis tem valor definido conforme o contexto.

Executando esse fluxo, temos:

image

Certo?

Condicionais

Não há como definir fluxos de atividades sem condicionais. Certo?! Observe:

private Activity CreateWorkflow3()
{
    var currentHour = new Variable<int>("hour", DateTime.Now.Hour);
    return new Sequence()
    {
        Variables = {
            currentHour,
        },
        DisplayName = "Main Sequence",
        Activities = {
            new WriteLine()
            {
                DisplayName="HelloWorld",
                Text="Hello, World from WF"
            },
            new WriteLine()
            {
                DisplayName="DisplayHour",
                Text= new InArgument<string>(ctx => currentHour.Get(ctx).ToString())
            },
            new If()
            {
                DisplayName = "Conditional Greeting",
                Condition=ExpressionServices.Convert<bool>( ctx =>
                    currentHour.Get(ctx) < 12),
                Then = new WriteLine() { Text = "Good morning!" },
                Else = new WriteLine() { Text = "Good afternoon" }
            }
        }
    };
}

Vejamos:

  • WF define atividades para execução de atividades – entre elas, If;
  • Há uma classe utilitária, chamada ExpressionServices, que ajuda na execução de expressões (necessária para a condição);
  • As propriedades Then e Else aceitam atividades, o que permite, inclusive, novas condicionais ou sequências complexas.

Loops

Tão importantes quanto condicionais, são os loops. Observe:

private Activity CreateWorkflow4()
{
    var counter = new Variable<int>("counter", DateTime.Now.Hour);
    return new Sequence()
    {
        Variables = {
            counter,
        },
        DisplayName = "Main Sequence",
        Activities = {
            new WriteLine()
            {
                DisplayName="HelloWorld",
                Text="Hello, World from WF"
            },
            new If()
            {
                DisplayName = "Conditional Greeting",
                Condition=ExpressionServices.Convert<bool>( ctx =>
                    counter.Get(ctx) < 12),
                Then = new WriteLine() { Text = "Good morning!" },
                Else = new WriteLine() { Text = "Good afternoon" }
            },
            new While()
            {
                DisplayName="Until counter > 0",
                Condition = ExpressionServices.Convert<bool>( ctx =>
                    counter.Get(ctx) > 0),
                Body = new Sequence()
                {
                   Activities = 
                   {
                       new WriteLine()
                       {
                           Text = new InArgument<string>(ctx => counter.Get(ctx).ToString())
                       },
                       new Delay()
                       {
                           Duration = TimeSpan.FromSeconds(1)
                       },
                       new Assign<int>()
                       {
                           DisplayName = "Decrementing counter",
                           Value = new InArgument<int>(ctx => 
                               counter.Get(ctx) - 1),
                            To = new OutArgument<int>(counter)
                       }
                   }
                }
            }
        }
    };
}

Perceba:

  • WF define uma atividades para execução de loops – entre elas, While;
  • Podemos determinar uma atividade como “corpo” do loop, inclusive novas sequências (o que fizemos);
  • As variáveis do fluxo podem ter seus valores modificados através de uma atividade de atribuição.

Executando o código, temos

image

Bacana, não?

Por agora, era isso.

Smiley piscando

Etiquetado:
Publicado em: Post