Workflow Foundation 4.0: Introduzione alle CTP Ottobre 2008
Autore: Roberto Brunetti - DevLeap
La prima novità che salta all’occhio è il nuovo designer, fatto in WPF e sicuramente per adesso non definitivo nella forma e colori.

La toolbox presenta molte activity aggiuntive, alcune delle quali tratteremo in questa breve introduzione.
Partiamo con la prima novità che riguarda le custom activity: in questo semplice progetto, come si nota, ho creato un progetto separato “CustomActivities” con due semplicissime activity: ReadLine e WriteLine; la prima ha il compito di leggere gli input dell’utente nel prompt dei comandi e la seconda di eseguire una Console.WriteLine.
Partiamo dalla seconda: per eseguire una WriteLine su Console l’activity ha bisogno del testo da visualizzare; in WF 4.0 è possibile definire i parametri di input e di output delle activity senza la necessità dover creare proprietà pubbliche o dependency property come avviene per la versione.
Questo il codice dell’activity:

E’ stato definito in parametro di input (InArgument), che, sfruttando i generics dichiara che il tipo di parametro è string. L’altra novità rispetto alla versione 3.0/3.5 è la derivazione della nostra activity da WorkflowElement e non da Activity.
L’override del metodo Execute sin dalla versione 3.0 consente di indicare il codice da eseguire durante l’esecuzione dell’attività e riceve come sempre l’AEC (Activity Execution Context); per recuperare il valore del parametro di input si esegure this.Text.Get passando sempre il Context. Visto che il tipo di parametro è string non occorrono altre operazioni per estrarre eventuali proprietà di una classe più complessa che potremmo ricevere.
Stesso ragionamento per il parametro di output dell’activity (anzi del WorkflowElement) ReadLine; ecco il codice:

In questo caso si usa il Set su outArguments passando sempre l’AEC ricevuto.
E’ possibile definire le activity interamente in XAML: ecco un esempio di activity che definisce tre parametri

Costruite le activity (in xaml o in code) siamo quindi pronti per il nostro primo worfklow 4.0 che può utilizzare in sequenza WriteLine e Readline per chiaccherare con l’utente tramite la console.
Ecco il flusso completo
![clip_image002[1]](http://devlab.devleap.it/robertob/immagini/posts/Workflow.0IntroduzionealleCTPOttobre2008_114BC/clip_image0021_thumb.jpg)
La prima WriteLine deve scrivere a video un messaggio che l’autore del workflow può impostare liberamente. Nelle proprietà dell’activity (come nella versione 3.x) è possibile indicare il valore del parametro direttamente nel designer:

La successiva ReadLine, come abbiamo visto nel codice, espone un parametro di output denominato outArgument che dovrà essere passato come parametro di input alla successiva WriteLine che semplicemente lo visualizzerà nella console.
In WF 3.x questo passaggio di informazioni fra una activity e l’altra era prossibile tramite il binding diretto fra la proprietà di una activity e la proprietà dell’altra activity: il meccanismo è sicuramente semplice ma crea un problema di “scope” ovvero: quando muoiono questa variabili esposte dalle activity ? Visto che teoricamente è possibile fare il binding fra activity presenti nell’intero workflow sequenzale, il runtime del workflow è costretto a tenere in vità questa variabili fino a che non termina l’instanza del workflow, cosa che causa anche un tempo di serializzazione/deserializzazione durante la persistenza del workflow.
In WF 4.0 è possibile creare variabile all’interno di uno scope: ad esempio è possibile definire una variabile a livello di sequence che quindi “nascerà” all’inizio della sequenza e “morirà” alla fine della stessa. Queste variabili sono poi bindable verso i parametri di input e output delle varie activity presenti nello stesso scope, ovvero nella sequence per proseguire con il nostro esempio.

La finestrina si apre dall’apposito pulsante nella toolbar in basso e consente di definire appunto le variabili visibli all’interno dello scope. La finestra mostra anche le variabili disponibili che arrivano da variabili definite nel contenitore dell’activity corrente: ad esempio inserendo una sequnce all’interno del nostro flusso principale (che a sua volta è una sequence) avremmo la visibilità della variabili della sequence padre anche sul figlio. Ecco la figurina esemplificativa:

Come si nota le variabili Destinazione (e Prezzo) sono disponibili anche nella sequence figlia e vengono separate come visualizzazione nella parte alta Variable Available.
Le variabili a livello di scope possono essere bindate ai parametri di input e output della varie activity: nel nostro caso quindi ospitiamo la destinazione del nostro volo nella variabile Destinazione durante la ReadLine e la WriteLine successiva. La Readline avrà il parametro di ouput legato alla Destinazione e la WriteLine avrà il parametro di input legato sempre a Destinazione.
Ecco la Readline:

Per adesso l’editor, in questa primissima CTP di Ottobre 2008, non fornisce nessun aiuto (tipo intellisense e enumeration) delle variabili disponibili.
La WriteLine nel nostro flusso farà esattamente la stessa cosa...o quasi. E’ possibile legare ad un parametro di input (o anche ad una variabile) una espressione: il binding quindi consente non solo di legare un parametro ad un altro, ma anche di eseguire una espressione durante il binding. Ad esempio la nostra WriteLine può contenere questo:

In questo semplice esempio abbiamo lavorato con delle stringhe, ma ovviamente il discorso è valido per altri tipi di dato: questo consente all’autore di workflow di eseguire espressioni (che vengono valutate dal runtime) per assegnare (o anche leggere) valori ai parametri o alle variabili.
Per avviare un workflow (o meglio una istanza di workflow) nel WF 3.0/3.5 occorre avviare il workflow runtime, configurarlo adeguatamente e poi chiedere al workflow runtime di creare e avviare una istanza. Questo il codice di una applicazione console per rimanere in tema:
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
#endregion
namespace _5_CodeCondition
{
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{waitHandle.Set();};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs
e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(_5_CodeCondition.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
}
}
}
Ecco il codice 4.0, direi molto più semplice per avviare la sequnce del nostro esempio.
namespace TravelSearchApplication
{
using System;
using System.Linq;
using System.Threading;
using System.WorkflowModel;
using System.WorkflowModel.Activities;
class Program
{
static void Main(string[] args)
{
Sequence s;
AutoResetEvent syncEvent = new AutoResetEvent(false);
WorkflowInstance myInstance = WorkflowInstance.Create(new Sequence1());
myInstance.Completed += delegate(object sender, WorkflowCompletedEventArgs e) { syncEvent.Set(); };
myInstance.Resume();
syncEvent.WaitOne();
}
}
}
Complicando leggermente il nostro esempio supponiamo di voler chiamare un servizio WCF che espone le informazioni sui voli in base alla destinazione scelta. A prescindere da come viene creato il servizio server-side (in WCF 4.0 è possibile definire un servizio interamente in xaml), tramite la ClientOperation possiamo indicare il binding, il contratto e la operation da eseguire:

Nel nostro caso usiamo BasicHttpBinding sull’endpoint localhost:8080/service1.xamlx (come accennavo il servizio è scritto interamente in xaml) utilizzando il contratto Contract1 sulla operazione GetData.
Anche in questo caso è possibile fare binding fra i parametri di ingresso e uscita dell’operation rispetto alla variabili del nostro scope:

Il servizio accetta in input il parametro String denominato Destination a cui è verrà legata la nostra ormai famosa variabile Destinazione e risponderà con un valore Double denominato Prezzo che viene legato alla nostra variabile Prezzo (che verrà poi usata per visualizzare il risultato).
L’ultima activity del nostro workflow non farà altro che passare la variabile Prezzo alla WriteLine con il metodo che ormai conosciamo:

Personalmente non mi è chiara una cosa: abbiamo dovuto usare VB.NET per eseguire l’espressione di conversione del Prezzo (Double) in String. Sulla documentazione attuale e alla Professional Developers Conference 2008 di Losa Angeles è stato indicato che le espressioni si scrivono appunto solo in VB.NET. Non ho capito se l’idea sarà questa anche per la versione finale del prodotto o semplicemente per adesso. My Fault.
Anche se l’articolo è su WF 4.0, vale la pena di dare un occhio a come ho realizzato il servizio server-side: no code ! La descrizione del contratto, delle operation e relativi parametri, nonchè le operazioni da eseguire a fronte di una chiamata sono definite in xaml. Ecco la parte server:

L’activity utilizzata descrivere il contratto è ServiceOperation ed è ospitata nel file service1.xamlx (la x finale indica al motore di esecuzione che si tratta di un servizio attivabile tramite WAS di IIS7).
Per descrivere il contratto (non è la maschera definitiva) si usa questa mascherina:

E scendendo in Edit sull’operazione GetData si vedono i parametri Destination e Prezzo che sonon stati usati nella client operation che abbiamo descritto prima:

E’ possibile indicare il flusso Two-way o One-way e il supporto alle transazioni, oltre ai criteri ovvi di security.
Torniamo a WF 4.0: per calcolare il prezzo, il nostro servizio deve eseguire alcune regole in base alla destinazione. Per questo è stato usato (sempre dentro la definizione xamlx del servizio che rappresenta i passi del workflow da eseguire all’arrivo di una richiesta di GetData) un nuovo componente di WF 4.0 denominato Flowchart.
Espandendo il Flowchart si ottiene questo:

Sul rombo con la X si indica la variabile su cui effettuare il test (nel nostro caso Destination) e ogni braccio del flowchart indica il valore che attiva il braccio stesso: nel nostro caso vediamo che sul primo braccio è indicato Brescia nella finestra delle proprietà:

Altra novità della versione 4.0 è l’activity di Assign che valorizza la variabile Price con un valore (o una espressione). Nel nostro caso quindi il volo su Brescia costra 1.000, mentre il volo su Firenze costa 500; negli altri casi si scatena una eccezione che poi verrà convertita in un SOAP Fault.
Una volta calcolato il prezzo base (1000 o 500 nel nostro caso) il flowchart riunisce il flusso per entrare in un RuleSet: chi conosce i RuleSet di Worklfow 3.x si troverà davanti un editor completamente diverso (e molto più intuitivo) che consente di validare una serie di regole (con sequence, chaining, full chaining e reevalutation).
Il nuovo editor si presenta così, e appunto anche in questa prima CTP è molto più intuitivo dell’attuale:

Le regole possono avere nomi descrittivi, il che aiuta molto nell’individuazione delle regole.
Una volta esplosa (nel senso buono J) una regola ottengo la visualizzazione delle espressioni di valutazione:

Se DiscountLevel = 1 Then DiscountPoint = 0.95. L’assegnazione si effettua sempre tramite l’activity di assign.
Per mettere in produzione il nostro servizio che comprende il workflow ci si può affidare alla feature di Dublin di cui trovate un articolo introduttivo sempre nella sezione articolidevleap del sito blogs.devleap.com.
Ecco l’url completo: http://blogs.devleap.com/articolidevleap/archive/2008/11/14/dublin-windows-application-server.aspx
Per un primo contatto su Visual Studio 2010 trovate i seguenti due articoli:
http://blogs.devleap.com/articolidevleap/archive/2008/10/17/visual-studio-team-system-2010-primo-contatto.aspx
http://blogs.devleap.com/articolidevleap/archive/2008/10/26/visual-studio-team-system-2010-primo-contatto-parte-2.aspx.
Roberto Brunetti