Sunday, July 6, 2008

Back State

. Sunday, July 6, 2008

Un workflow sequenziale come suggerisce la parola stessa scorre linearmente eseguendo uno dopo l'altro gli activity che costituiscono il flusso.
In questi giorni però mi son ritrovato a sviluppare un sistema che permettesse al cliente per cui sto lavorando, di effettuare un ritorno indietro in un workflow sequenziale composto da activity long running.
Detto con un esempio, se il workflow sequenziale è composto dai seguenti tre activity A, B e C, e il flusso si trova "persistito nell'activity B", allora deve essere possibile ritornare in A.
Nel caso di una macchina a stati la cosa sarebbe stata di semplice implementazione, poichè il flusso è costituito da un'insieme di stati collegati fra loro da una serie di eventi che determinano il passaggio da uno stato ad un altro.
Inoltre tramite il metodo SetState è possibile effettuare una transazione indicando il nome dello stato di destinazione:

   1: StateMachineWorkflowInstance ins = new StateMachineWorkflowInstance(runtime, instance.InstanceId);
   2: ins.SetState("TargetState");

Purtroppo nel mio caso la strada di "trasformazione" dell'attuale flusso sequenziale in una macchina a stati non è di facile attuazione, anche se molto probabilmente si tratta della soluzione migliore.

Quindi ho pensato di preservare man mano che si effettua il passaggio tra gli activity, lo stato del workflow in una tabella history, recuperando dalla tabella InstaceState; il blob, serializzato in BinaryFormatter e compresso tramite Gzip, contenuto nel campo State.
BackWorkflow1La tabella "base" presenta l'identificativo del flusso, lo stato ed infine un campo identity che indica l'ordine di inserimento.
A questo punto possiamo espandere il codice del custom activity visto nel post precedente, aggiungendo la logica del salvataggio dello stato del workflow:

   1: protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
   2: {
   3:   WorkflowQueuingService queueService = executionContext.GetService<WorkflowQueuingService>();
   4:   WorkflowQueue wfQueue = queueService.CreateWorkflowQueue(this.Name, true);
   5:   wfQueue.QueueItemAvailable += new EventHandler<QueueEventArgs>(nextActivity);
   6:   return ActivityExecutionStatus.Executing;
   7: }
   8:  
   9: void nextActivity(object sender, QueueEventArgs e)
  10: {
  11:   ActivityExecutionContext context = (ActivityExecutionContext)sender;
  12:   WorkflowQueuingService queueService = context.GetService<WorkflowQueuingService>();
  13:   queueService.DeleteWorkflowQueue(e.QueueName);
  14:   /*
  15:   * Recupero lo stato del workflow dalla tabella InstanceState
  16:   */
  17:             
  18:   /*
  19:   * Insert dello stato del workflow nella tabella history
  20:   */
  21:   context.CloseActivity();
  22: }

Da tale processo escludo i custom activity incapsulati in un branch ParallelActivity.
Per indicare quali activity devono partecipare al processo utilizzo un DependencyProperty:

   1: public static DependencyProperty ActivePersistenceProperty = 
      DependencyProperty.Register("ActivePersistence", typeof(Boolean), typeof(WorkflowClasse));
   2:  
   3: [DescriptionAttribute("ActivePersistence")]
   4: [CategoryAttribute("ActivePersistence Category")]
   5: [BrowsableAttribute(true)]
   6: [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
   7: public Boolean ActivePersistence
   8: {
   9:     get
  10:     {
  11:         return ((Boolean)(base.GetValue(Workflow1.ActivePersistenceProperty)));
  12:     }
  13:     set
  14:     {
  15:         base.SetValue(Workflow1.ActivePersistenceProperty, value);
  16:     }
  17: }

Per completare il giro è necessario verificare nel metodo nextActivity, se la property ActivePersistence è settata con il valore true.  
E'  facile immaginare come, per effettuare un ritorno in uno "stato" precedente basta effettuare l'update del campo State della tabella InstanceState; dalla tabella History.
Ovviamente sono molto curioso di sapere se esiste una soluzione più elegante da poter adottare in questo preciso contesto, in caso contrario spero che questo post possa essere d'aiuto, o quantomeno di ispirazione a qualcun altro.

0 commenti:

Post a Comment