in

DotNetSide

Dot Net South Italy Developers User Group

Articoli

Articoli pubblicati dagli iscritti a .netSide

Invocare un workflow in modalità sincrona

di Fabio Cozzolino

Introduzione

Nell'uso di Workflow Foundation (WF), come di qualsiasi altra tecnologia, ci rendiamo conto di come il comportamento di default non sempre va incontro alle nostre effettive esigenze. Fortunatamente i framework sono fatti per essere estesi e modellati per risolvere le specifiche problematiche dei vari scenari in cui vengono utilizzati. In questo articolo vedremo come sfruttare alcune caratteristiche di WF per modificare il normale flusso di chiamata tra due diverse istanze di workflow.

Un workflow chiama l'altro

Nel nostro progetto di esempio abbiamo due workflow: Workflow1 e Workflow2. Il primo esegue il suo lavoro e ad un certo punto chiama il secondo. La chiamata al secondo workflow avviene attraverso l'utilizzo della InvokeWorkflowActivity:

Workflow1 Workflow2
image image

L'esecuzione del workflow ci consente di ottenere questo risultato:

image

Come possiamo notare il Workflow1 termina la sua esecuzione prima del Workflow2. Il comportamento non è errato, anzi, dato che l'esecuzione è asincrona e l'InvokeWorkflowActivity si limita a richiedere al runtime l'esecuzione di un nuovo workflow eventualmente passando in input i parametri necessari.

L'obiettivo dell'articolo è quello di arrivare a controllare l'ordine di esecuzione di questo processo. In sostanza vogliamo che il Workflow1 venga notificato nel momento in cui il Workflow2 termina la sua esecuzione. Il Workflow2, quindi, al termine del suo flusso genererà l'evento di notifica gestito dal Workflow1. Per il nostro scopo utilizzeremo l'HandleExternalEventActivity e la CallExternalMethodActivity. Affinchè queste due activity funzionino correttamente, utilizzeremo il servizio ExternalDataExchangeService.

Definizione di metodi ed eventi esterni

Il servizio ExternalDataExchangeService consente di "agganciare" al runtime l'esecuzione di metodi ed eventi da classi esterne al contesto dei workflow. Per prima cosa definiamo l'interfaccia della classe che andremo a creare:

  1:     [ExternalDataExchange]
  2:     internal interface ICallbackService
  3:     {
  4:         event EventHandler<CallbackServiceEventArgs> WorkflowCompleted;
  5:         void CompleteWorkflow(Guid requestorId);
  6:     }

Definiamo qui un metodo ed un evento. Il metodo sarà quello invocato dal Workflow2 per notificare la corretta conclusione della sua esecuzione, mentre l'evento WorkflowCompleted sarà l'evento gestito dal Workflow1 per attendere la conclusione del workflow invocato. L'interfaccia deve essere necessariamente marcata con l'attributo ExternalDataExchange per essere poi gestita dal relativo servizio ExternalDataExchangeService.

E' importante notare, però, un'ultima cosa ma molto importante. Il metodo CompleteWorkflow accetta in input un parametro di tipo Guid chiamato requestorId. Questo parametro conterrà l'identificativo univoco del workflow richiedente, e quindi del Workflow1. Questo permetterà al runtime di identificare correttamente l'istanza del workflow che deve essere invocata per gestire l'evento WorkflowCompleted. Questo è un passaggio determinante per il corretto funzionamento del progetto.

L'identificativo del richiedente sarà passato all'evento attraverso l'utilizzo ExternalDataEventArgs o, meglio, di una estensione di questa particolare classe necessaria per la definizione di alcuni parametri utili al contesto di esecuzione. Per approfondimenti vi rimando alla documentazione ufficiale.

Implementiamo quindi la CallbackServiceEventArgs:

  1:     [Serializable]
  2:     public class CallbackServiceEventArgs : ExternalDataEventArgs
  3:     {
  4:         public CallbackServiceEventArgs(Guid instanceId) : base(instanceId)
  5:         {
  6:         }
  7: 
  8:         public CallbackServiceEventArgs(Guid instanceId, IPendingWork workHandler, object workItem, bool waitForIdle) : base(instanceId, workHandler, workItem, waitForIdle)
  9:         {
 10:         }
 11: 
 12:         public CallbackServiceEventArgs(Guid instanceId, IPendingWork workHandler, object workItem) : base(instanceId, workHandler, workItem)
 13:         {
 14:         }
 15:     }

Infine implementiamo l'interfaccia ICallbackService precedentemente creata:

  1:     [Serializable]
  2:     public class CallbackService : ICallbackService
  3:     {
  4:         public event EventHandler<CallbackServiceEventArgs> WorkflowCompleted;
  5: 
  6:         public void CompleteWorkflow(Guid requestorId)
  7:         {
  8:             if (WorkflowCompleted != null)
  9:             {
 10:                 var eventArgs = new CallbackServiceEventArgs(requestorId);
 11:                 WorkflowCompleted(this, eventArgs);
 12:             }            
 13:         }
 14:     }

Il metodo CompleteWorkflow, come già detto, si occupa semplicemente di invocare l'evento WorkflowCompleted passando tra gli argomenti l'identificativo del workflow richiedente.

Gestiamo gli eventi

A questo punto il codice è pronto e dobbiamo semplicemente utilizzare i servizi dai nostri workflow. Prima di procedere con l'utilizzo delle activity, dobbiamo necessariamente aggiungere una proprietà al Workflow2. Apriamo quindi il sorgente del workflow ed aggiungiamo la proprietà WorkflowRequestorId:

  1: public Guid WorkflowRequestorId 
  2: { 
  3:     get; 
  4:     set; 
  5: }

La proprietà WorkflowRequestorId contiene l'identificativo dell'istanza del workflow chiamante e la utilizzeremo per consentire al runtime di invocare il gestore dell'evento nel workflow corretto. Dopo aver creato la proprietà, apriamo quindi il Workflow2 ed aggiungiamo al termine del flusso l'activity CallExternalMethodActivity.

image

Settiamo quindi le proprietà selezionando l'interfaccia del servizio che dobbiamo utilizzare ed il metodo che dobbiamo invocare. Infine impostiamo il binding del parametro requestorId sulla proprietà WorkflowRequestorId precentemente creata e che contiene l'identificativo dell'istanza del workflow chiamante:

image

A questo punto il Workflow2 è completo e come potete vedere l'activity callExternalMethodActivity1 invocherà il metodo CompleteWorkflow passando come parametro il contenuto della proprietà WorkflowRequestorId.

Allo stesso modo nel Workflow1 inseriamo l'attività HandleExternalEventActivity ed impostiamo le proprietà per gestire l'evento WorkflowCompleted impostato nella proprietà WorkflowCompleted.

image image

Prima di completare, però, modifichiamo la chiamata al Workflow2 impostando la proprietà WorkflowRequestorId con l'identificativo dell'istanza del Workflow1. Per farlo abbiamo bisogno di una ulteriore proprietà nel Workflow1 che ci consente di accedere alla proprietà WorkflowInstanceId:

  1: public sealed partial class Workflow1 : SequentialWorkflowActivity
  2: {
  3:     public Guid CurrentWorkflowId
  4:     {
  5:         get
  6:         {
  7:             return WorkflowInstanceId;
  8:         }
  9:     }
 10: }

Clicchiamo quindi sull'activity InvokeWorkflowActivity e colleghiamo la proprietà WorkflowRequestorId del workflow di destinazione con la proprietà CurrentWorkflowId:

image

Per poter utilizzare i servizi, infine, abbiamo bisogno di aggiungere l'istanza della classe CallbackService al runtime di workflow prima di avviarne l'esecuzione:

  1: class Program
  2: {
  3:     static void Main(string[] args)
  4:     {
  5:         var runtime = new WorkflowRuntime();
  6:         
  7:         var edes = new ExternalDataExchangeService();
  8:         runtime.AddService(edes);
  9: 
 10:         var callbackService = new CallbackService();
 11:         edes.AddService(callbackService);
 12:         
 13:         var instance = runtime.CreateWorkflow(typeof (Workflow1));
 14:         instance.Start();
 15: 
 16:         Console.WriteLine("Runtime Started");
 17:         Console.ReadLine();
 18: 
 19:         Console.WriteLine("Runtime Completed");
 20:         Console.ReadLine();
 21:     }
 22: }

A questo punto siamo pronti ad eseguire la nostra applicazione e vediamo che cosa accade:

image

Notiamo che ora il messaggio Workflow1 Completed viene scritto nell'output della console subito dopo il messaggio Workflow2 Completed. Il Workflow1 ha atteso che il Workflow2 terminasse.

Conclusioni

In questo articolo abbiamo visto come sfruttare il meccanismo dei servizi per consentire l'esecuzione del flusso dei nostri workflow secondo le diverse necessità che uno o più scenari possono richiedere. La soluzione può sicuramente ancora essere estesa e migliorata, resa magari più flessibile. Nel frattempo vi lascio con una serie di riferimenti per approfondire le tematiche che abbiamo affrontato.

Riferimenti

ExternalDataExchangeService
InvokeWorkflowActivity
CallExternalMethodActivity
HandleExternalEventActivity

Only published comments... Mar 10 2009, 03:00 PM by DotNetSide Staff
Filed under:

Comments

 

BlogServiceHost.Create() said:

Ho da poco pubblicato un articolo su come invocare workflow in modalità sincrona . Leggetelo e magari

March 11, 2009 3:48 PM
Powered by Community Server (Commercial Edition), by Telligent Systems