in

DotNetSide

Dot Net South Italy Developers User Group

Articoli

Articoli pubblicati dagli iscritti a .netSide

SqlWorkflowPersistenceService: il servizio di persistenza di Workflow Foundation

Autore: Michele Locuratolo (Mighell)

WinFX, tra le tante novità, introduce una "piattaforma" per la creazione e l’esecuzione di Workflow. La scelta di creare Windows Workflow Foundation nasce dall’esigenza di risolvere alcune delle problematiche insite in questa categoria di applicazioni e di farlo con un sistema omogeneo, integrato in un framework che è WinFX e con un unico tool di svilupp Visual Studio .NET 2005.
In questo articolo analizzeremo uno di questi problemi e vedremo insieme la soluzione proposta da Microsoft.

 

Il problema: la persistenza
Uno dei problemi più noti a chi ha sviluppato un workflow è la persistenza. Un workflow infatti, per sua natura, può essere eseguito per passi e durare molto a lungo. Pensiamo all’esempio più classico: la gestione di un ticket.
Un operatore riceve la segnalazione di un utente e crea un ticket. Il ticket generato dovrà poi essere risolto da un altro operatore che in quel momento potrebbe essere impegnato o addirittura assente. In alcuni casi, invece, l’evasione del ticket potrebbe richiedere più tempo del previsto. Proviamo a gestire il tutto sfruttando le potenzialità di un workflow.
Un esempio del workflow che potrebbe essere demandato alla gestione dei ticket è visibile in Figura 1.

 

 

 

workflow

 

 

 

 

Cosa succede quindi al nostro workflow?
In una situazione normale, esso resterebbe attivo in memoria in attesa di essere completato. Ma, al vantaggio di essere immediatamente disponibile per il completamento, si aggiungono numerosi svantaggi. Innanzitutto, l’occupazione di memoria, l’impossibilità di sopravvivere ad un riavvio della macchina, impossibilità di essere recuperato in caso di guasto hardware etc.
Per risolvere questi problemi, nel runtime di Windows Workflow Foundation è stato incluso un meccanismo di persistenza denominato SqlWorkflowPersistenceService.
Vediamo insieme di cosa si tratta.

 

 

 

 

 

SqlWorkflowPersistenceService
SqlWorkflowPersistenceService è il servizio realizzato per permettere la serializzazione di una istanza di un determinato workflow, su un Data Base SQL Server 2005 (o SQL Express).
Il suo uso di base è decisamente semplice: allo scattare di un determinato evento (vedremo poi quali eventi sono i più indicati), l’istanza del workflow viene serializzata su un Data Base ed eliminata dalla memoria.
La serializzazione avviene previa creazione di un apposito data base pronto ad ospitare il workflow. La creazione di tale DB va fatta eseguendo gli script presenti nella cartella %windir% WinFX.0Workflow Foundation_Schema.sql e SqlPersistenceService_Logic.sql .

Nel nostro DB Engine, verrà creato un semplice Data Base rappresentato in Figura 2 ed una serie di stored procedure richiamate direttamente dal meccanismo di persistenza.
Creato il Data Base, non ci resta che implementare il meccanismo di persistenza nella nostra applicazione.

 

 

 

Implementazione di SqlWorkflowPersistenceService
Come accennato nel paragrafo precedente, prima di persistere il workflow sul Data Base, bisogna individuare il momento più opportuno per farlo. Di certo non vogliamo che esso venga "congelato" durante l’esecuzione!

Il momento sicuramente più opportuno è quando il workflow è libero, e cioè quando l’esecuzione non è ancora terminata (non avrebbe senso serializzarlo se è terminato) e nessuna activity è ancora in esecuzione. Condizione che, riprendendo il workflow di Figura 1, si ha nel blocco WaitForOperator.

Questo istante viene notificato dall’evento WorkflowIdled dell’oggetto WorkflowRuntime. La prima cosa da fare quindi, è creare nella nostra applicazione un gestore per questo evento. Lo facciamo con il seguente codice:

 

_runtime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(_runtime_WorkflowIdled);

Il metodo _runtime_WorkflowIdled è implementato come segue:

 

 

 

void _runtime_WorkflowIdled(object sender, WorkflowEventArgs e) {
    System.Threading.ThreadPool.QueueUserWorkItem(UnloadInstance, e.WorkflowInstance);
}


che a sua volta richiamerà il metodo

 

 

 

private void UnloadInstance(object workflowInstance) {
    ((WorkflowInstance)workflowInstance).TryUnload();
}

La serializzazione vera e propria, avviene con la chiamata al metodo TryUnload(), sempre dell’oggetto WorkflowRuntime.

La decisione di passare attraverso un ulteriore metodo (UnloadInstance) tramite una chiamata a System.Threading.ThreadPool.QueueUserWorkItem(), è dovuta alla modalità di esecuzione del runtime, che avviene in un thread diverso da quello dell’oggetto chiamante (nel caso specifico si tratta si una applicazione Windows).

 

Il workflow è serializzato. Ed ora?
Dopo aver serializzato la nostra istanza del Workflow, non ci resta che recuperarla all’occorrenza e completare l’esecuzione del flusso.
Per farlo, useremo il metodo GetWorkflow() dell’oggetto WorkflowInstance che, a differenza del metodo CreateWorkflow(), recupera l’istanza il cui GUID è passato come argomento del metodo, anziché crearne una nuova.
Il codice per eseguire l’operazione è il seguente:

 

 

 

private void ResumeInstance(string workflowID) {
    Guid id = 
new 
Guid(workflowID);
    _wfinstance = _runtime.GetWorkflow(id);
    _wfinstance.Load();
}

Da questo momento, sarà possibile continuare ad eseguire il Workflow dal punto in cui era stato serializzato.

 

 

SqlWorkflowPersistenceService behind the scenes
Tanta semplicità nella gestione della persistenza nasconde però un lungo lavoro svolto dietro le quinte dal runtime. Documentazione alla mano, e munendosi del tool Reflectorsi possono analizzare i meccanismi che vengono eseguiti internamente da workflow. La prima cosa interessante da notare e anche ben documentata è il fatto che SqlWorkflowPersistenceService deriva da una classe base chiamata WorkflowPersistenceService. Se ne deduce che è possibile creare dei meccanismi di persistenza personalizzati e svincolati da SQL 2005. Tratteremo meglio, in un prossimo articolo, la creazione di meccanismi personalizzati di persistenza.
Ma vediamo meglio cosa accade quanto richiamiamo il metodo TryUnload().
Viene internamente richiamato il corrispettivo di pari nome dell’oggetto WorkflowExecutor, una classe marcata come internal sealed e che, a quanto pare, si occupa proprio della gestione dell’esecuzione del workflow.
Il metodo TryUnload di WorkflowExecutor, dopo una serie di controlli, richiama a sua volta il metodo bool PerformUnloading() che, dopo aver sollevato l’evento WorkflowEventInternal.Unloading, richiamato il metodo TimerQueue.SuspendDelivery() e settato a false il valore di IsInstanceValid, scatena un nuovo evento denominato FireEventAfterSchedulerLokDrop. A questo punto, è facile immaginare (la documentazione è inesistente), che il servizio di persistenza si registri (non direttamente) a questo evento ed esegua il lavoro concreto.
Tornando alla SqlWorkflowPersistenceService, salta all’occhio la presenza di due metodi  denominati SaveWorkflowInstanceState(Activity rootActivity, bool unlock) e SaveCompletedContextActivity(Activity completedScopeActivity) che richiamano il metodo   

 

 

protected static byte[] GetDefaultSerializedForm(Activity activity)
 
della classe base WorkflowPersistenceService al cui interno troviamo finalmente:

 

activity.Save(stream2);
che a sua volta richiama il metodo Save:       

 

public void Save(Stream stream){
      
this
.Save(stream, Activity.binaryFormatter);
}

A questo punto, attraverso l’interfaccia IFormatter, le activity presenti nel nostro workflow vengono predisposte per la serializzazione.Un ultimo punto da chiarire è come e quando realmente vengono richiamate le stored procedure per la serializzazione.
Se osserviamo la definizione della classe SqlWorkflowPersistenceService, vediamo che essa deriva da SqlWorkflowPersistenceService ed implementa l’interfaccia    
IPendingWorks. Come si evince dalla documentazione, essa si occupa di partecipare ai processi di batch di Workflow. Il metodo Commit, implementato in SqlWorkflowPersistenceService, richiama infatti il metodo InsertInstanceState di una istanza della classe PersistenceDBAccessor. All’interno di tale metodo si trovano finalmente le istruzioni per la serializzazione delle activity nel Data Base. Per la precisione, all’istanza di PersistenceDBAccessor, viene passato, tra gli argomenti, un elemento della collection PendingWorkItem  di cui si legge la proprietà SerializedActivity che rappresenta l’activity preparata per la serializzazione.      
E’ importante sottolineare che, se la proprietà WorkflowInstance.WorkflowStatusProperty è Completed, sarà possible eliminare i dati serializzati dalla tabella nel DB. In alternativa, la cosa più sensata è quella di  sottoscriversi all’evento WorkflowCompleted e pulire automaticamente il Data Base.

Per il momento è tutto. Al prossimo articolo.

Only published comments... May 08 2006, 11:49 PM by Anonymous
Filed under:

Comments

 

Mighell's blog said:

Dopo i postumi del workshop, questa sera mi sono dedicato alla stesura si un articolo per innaugurare...
May 9, 2006 12:26 AM
 

Mighell's Blog said:

May 9, 2006 9:58 AM
 

Blog di Vito Arconzo said:

May 9, 2006 12:58 PM
 

Blog di Vito Arconzo said:

May 9, 2006 1:00 PM
 

Mighell's blog said:

L'altro giorno, poco prima della mia sessione su Workflow all'evento .netSide, la mia demo aveva smesso...
May 9, 2006 5:37 PM
 

Mighell's blog said:

Oggi girovagavo un po’ senza meta tra i settaggi di Community Server (su cui gira .netSide) e sono andato...
June 4, 2006 1:05 PM
 

Mighell's Blog said:

June 4, 2006 1:06 PM
 

Mighell's blog said:


Oggi girovagavo un po’ senza meta tra i settaggi di Community Server (su cui gira .netSide) e sono...
June 4, 2006 1:09 PM
 

Articoli said:

Autore: Michele Locuratolo (Mighell) Uno dei (tanti) pregi di Windows Workflow Foundation sono i servizi

February 15, 2007 12:58 PM
 

Articoli said:

Autore: Michele Locuratolo (Mighell)Uno dei (tanti) pregi di Windows Workflow Foundation sono i servizi

February 15, 2007 1:19 PM
 

Semplice said:

Link persistenza workflow

October 29, 2007 10:08 PM
Powered by Community Server (Commercial Edition), by Telligent Systems