Condividi tramite


Introduzione a Windows Workflow Foundation (WF) in .NET 4

Matt Milner, Pluralsight

Novembre 2009

Aggiornamento alla versione: aprile 2010

Panoramica

Come sanno gli sviluppatori di software, la scrittura di applicazioni può essere complessa e stiamo costantemente cercando strumenti e framework per semplificare il processo e aiutarci a concentrarsi sulle sfide aziendali che stiamo cercando di risolvere.  È stato spostato dalla scrittura di codice in linguaggi di computer come assembler a linguaggi di livello superiore, come C# e Visual Basic, che semplificano lo sviluppo, eliminano problemi di livello inferiore, ad esempio la gestione della memoria e aumentano la produttività degli sviluppatori.  Per gli sviluppatori Microsoft, il passaggio a .NET consente a Common Language Runtime (CLR) di allocare memoria, pulire gli oggetti non necessarie e gestire costrutti di basso livello come puntatori. 

Gran parte della complessità di un'applicazione risiede nella logica e nell'elaborazione che continua dietro le quinte.  Problemi come l'esecuzione asincrona o parallela e il coordinamento generale delle attività per rispondere alle richieste degli utenti o alle richieste di servizio possono portare rapidamente gli sviluppatori di applicazioni a eseguire il back-down nella codifica di basso livello di handle, callback, sincronizzazione e così via. Gli sviluppatori hanno bisogno della stessa potenza e flessibilità di un modello di programmazione dichiarativo per gli interni di un'applicazione, come per l'interfaccia utente in Windows Presentation Foundation (WPF). Windows Workflow Foundation (WF) fornisce il framework dichiarativo per la creazione di logica di applicazioni e servizi e offre agli sviluppatori un linguaggio di livello superiore per la gestione di attività asincrone, parallele e altre elaborazioni complesse.

Avere un runtime per gestire la memoria e gli oggetti ci ha liberato di concentrarsi maggiormente sugli aspetti aziendali importanti della scrittura del codice.  Analogamente, avere un runtime in grado di gestire le complessità del coordinamento del lavoro asincrono offre un set di funzionalità che migliorano la produttività degli sviluppatori.  WF è un set di strumenti per dichiarare il flusso di lavoro (logica di business), le attività che consentono di definire la logica e il flusso di controllo e un runtime per l'esecuzione della definizione dell'applicazione risultante.  In breve, WF sta usando un linguaggio di livello superiore per la scrittura di applicazioni, con l'obiettivo di rendere gli sviluppatori più produttivi, applicazioni più facili da gestire e modificare più rapidamente da implementare.  Il runtime di WF non solo esegue automaticamente i flussi di lavoro, fornisce anche servizi e funzionalità importanti durante la scrittura della logica dell'applicazione, ad esempio la persistenza dello stato, il segnalibro e la ripresa della logica di business, che portano a thread e agilità dei processi che consentono di aumentare e aumentare le prestazioni dei processi aziendali. 

Per altre informazioni concettuali sull'uso di WF per creare le applicazioni, è consigliabile leggere "The Workflow Way" di David Chappell, disponibile nella sezione Risorse aggiuntive. 

Novità di WF4

Nella versione 4 di Microsoft® .NET Framework, Windows Workflow Foundation introduce una notevole modifica rispetto alle versioni precedenti della tecnologia fornita come parte di .NET 3.0 e 3.5.  In effetti, il team ha rivisitato il nucleo del modello di programmazione, del runtime e degli strumenti e ha riprogettato ognuno di essi per aumentare le prestazioni e la produttività, nonché per affrontare i feedback importanti ottenuti dagli engagement dei clienti usando le versioni precedenti.  Le modifiche significative apportate sono state necessarie per offrire l'esperienza migliore per gli sviluppatori che adottano WF e per consentire a WF di continuare a essere un componente di base solido che è possibile sviluppare nelle applicazioni. Presenterò i cambiamenti di alto livello qui, e in tutto il documento ogni argomento otterrà un trattamento più approfondito. 

Prima di continuare, è importante comprendere che la compatibilità con le versioni precedenti era anche un obiettivo chiave in questa versione.  I nuovi componenti del framework si trovano principalmente negli assembly System.Activities.*, mentre i componenti del framework compatibili con le versioni precedenti si trovano negli assembly System.Workflow.*.  Gli assembly System.Workflow.* fanno parte di .NET Framework 4 e offrono una compatibilità completa con le versioni precedenti, in modo da poter eseguire la migrazione dell'applicazione a .NET 4 senza modifiche al codice del flusso di lavoro. In questo documento userò il nome WF4 per fare riferimento ai nuovi componenti trovati negli assembly System.Activities.* e WF3 per fare riferimento ai componenti presenti negli assembly System.Workflow.*. 

Designer

Una delle aree di miglioramento più visibili è nella finestra di progettazione del flusso di lavoro. L'usabilità e le prestazioni sono stati obiettivi chiave per il team per la versione di VS 2010.  La finestra di progettazione supporta ora la possibilità di lavorare con flussi di lavoro molto più grandi senza una riduzione delle prestazioni e i progettisti sono tutti basati su Windows Presentation Foundation (WPF), sfruttando appieno l'esperienza utente avanzata che è possibile creare con il framework dichiarativo dell'interfaccia utente.  Gli sviluppatori di attività useranno XAML per definire il modo in cui le attività appaiono e interagiscono con gli utenti in un ambiente di progettazione visiva.  Inoltre, il rehosting della finestra di progettazione del flusso di lavoro nelle proprie applicazioni per consentire agli utenti non sviluppatori di visualizzare e interagire con i flussi di lavoro è ora molto più semplice. 

Flusso di dati

In WF3 il flusso di dati in un flusso di lavoro era opaco.  WF4 fornisce un modello chiaro e conciso per il flusso di dati e l'ambito nell'uso di argomenti e variabili.  Questi concetti, familiari con tutti gli sviluppatori, semplificano sia la definizione dell'archiviazione dei dati, sia il flusso dei dati all'interno e all'esterno di flussi di lavoro e attività.  Il modello di flusso di dati rende inoltre più evidenti gli input e gli output previsti di una determinata attività e migliora le prestazioni del runtime man mano che i dati sono più facilmente gestiti. 

Organigramma

È stata aggiunta una nuova attività del flusso di controllo denominata Diagramma di flusso per consentire agli sviluppatori di usare il modello Diagramma di flusso per definire un flusso di lavoro.  Il diagramma di flusso è più simile ai concetti e ai processi di pensiero che molti analisti e sviluppatori passano durante la creazione di soluzioni o la progettazione di processi aziendali.  Pertanto, ha senso fornire un'attività per semplificare la modellazione del pensiero concettuale e della pianificazione già eseguiti.  Il diagramma di flusso consente concetti come il ritorno ai passaggi precedenti e la logica di suddivisione in base a una singola condizione o a una logica Switch/Case. 

Modello di programmazione

Il modello di programmazione WF è stato rinnovato per renderlo sia più semplice che più affidabile.  L'attività è il tipo di base principale nel modello di programmazione e rappresenta sia flussi di lavoro che attività.  Inoltre, non è più necessario creare un oggetto WorkflowRuntime per richiamare un flusso di lavoro, è sufficiente creare un'istanza ed eseguirla, semplificando gli scenari di unit test e applicazione in cui non si vuole risolvere i problemi di configurazione di un ambiente specifico.  Infine, il modello di programmazione del flusso di lavoro diventa una composizione completamente dichiarativa delle attività, senza codice accanto, semplificando la creazione del flusso di lavoro.

Integrazione di Windows Communication Foundation (WCF)

I vantaggi di WF si applicano sicuramente sia alla creazione di servizi che all'utilizzo o al coordinamento delle interazioni con i servizi.  Si è cercato di migliorare l'integrazione tra WCF e WF.  Le nuove attività di messaggistica, la correlazione dei messaggi e il supporto dell'hosting migliorato, insieme alla definizione del servizio completamente dichiarativo sono le principali aree di miglioramento. 

Introduzione al flusso di lavoro

Il modo migliore per comprendere WF consiste nell'iniziare a usarlo e ad applicare i concetti.  Verranno illustrati diversi concetti di base relativi alle basi del flusso di lavoro, quindi verranno illustrati alcuni semplici flussi di lavoro per illustrare il modo in cui questi concetti sono correlati tra loro. 

Struttura del flusso di lavoro

Le attività sono i blocchi predefiniti di WF e tutte le attività derivano dall'attività.  Nota terminologia: le attività sono un'unità di lavoro in WF. Le attività possono essere composte insieme in attività più grandi. Quando un'attività viene usata come punto di ingresso di primo livello, viene chiamata "Flusso di lavoro", proprio come Main è semplicemente un'altra funzione che rappresenta un punto di ingresso di primo livello per i programmi CLR.   Ad esempio, la figura 1 mostra un flusso di lavoro semplice compilato nel codice. 

Sequenza s = nuova sequenza

{

    Attività = {

        new WriteLine {Text = "Hello"},

        new Sequence {

            Attività =

            {

                new WriteLine {Text = "Workflow"},

                new WriteLine {Text = "World"}

            }

        }

    }

};

Figura 1: Flusso di lavoro semplice

Si noti nella figura 1 che l'attività Sequence viene usata come attività radice per definire lo stile del flusso di controllo radice per il flusso di lavoro. Qualsiasi attività può essere usata come radice o flusso di lavoro ed eseguita, anche una semplice WriteLine.   Impostando la proprietà Activities nella sequenza con una raccolta di altre attività, è stata definita la struttura del flusso di lavoro.  Inoltre, le attività figlio possono avere attività figlio, creando un albero di attività che costituiscono la definizione complessiva del flusso di lavoro. 

Modelli di flusso di lavoro e Progettazione flussi di lavoro

WF4 viene fornito con molte attività e Visual Studio 2010 include un modello per la definizione delle attività. Le due attività più comuni usate per il flusso di controllo radice sono Sequence e Flowchart.  Anche se qualsiasi attività può essere eseguita come flusso di lavoro, queste due forniscono i modelli di progettazione più comuni per la definizione della logica di business.   La figura 2 mostra un esempio di modello di flusso di lavoro sequenziale che definisce l'elaborazione di un ordine ricevuto, salvato e quindi le notifiche inviate ad altri servizi.   

 

figura 2: progettazione sequenziale del flusso di lavoro

Il tipo di flusso di lavoro Diagramma di flusso viene introdotto in WF4 per rispondere alle richieste comuni degli utenti esistenti, ad esempio per tornare ai passaggi precedenti in un flusso di lavoro e perché è più simile alla progettazione concettuale eseguita dagli analisti e dagli sviluppatori che lavorano sulla definizione della logica di business.  Si consideri, ad esempio, uno scenario che coinvolge l'input dell'utente per l'applicazione.  In risposta ai dati forniti dall'utente, il programma deve continuare nel processo o tornare a un passaggio precedente per richiedere nuovamente l'input. Con un flusso di lavoro sequenziale, questo implica un aspetto simile a quello illustrato nella figura 3 in cui viene usata un'attività DoWhile per continuare l'elaborazione fino a quando non viene soddisfatta una condizione. 

figura 3: sequenziale per la diramazione delle decisioni

La progettazione nella figura 3 funziona, ma come sviluppatore o analista che esamina il flusso di lavoro, il modello non rappresenta la logica o i requisiti originali descritti.   Il flusso di lavoro diagramma di flusso nella figura 4 offre un risultato tecnico simile al modello sequenziale usato nella figura 3, ma la progettazione del flusso di lavoro corrisponde più strettamente al pensiero e ai requisiti come originariamente definito. 

Figura 4: Flusso di lavoro diagramma di flusso di lavoro

Flusso di dati nei flussi di lavoro

Il primo pensiero che la maggior parte delle persone hanno quando pensano al flusso di lavoro è il processo aziendale o il flusso dell'applicazione.  Tuttavia, altrettanto critico come il flusso è costituito dai dati che determinano il processo e le informazioni raccolte e archiviate durante l'esecuzione del flusso di lavoro.  In WF4 l'archiviazione e la gestione dei dati è stata un aspetto fondamentale della progettazione. 

Esistono tre concetti principali da comprendere per quanto riguarda i dati: variabili, argomenti ed espressioni.  Le definizioni semplici per ognuna sono che le variabili sono per l'archiviazione dei dati, gli argomenti sono per il passaggio dei dati e le espressioni sono per la modifica dei dati. 

Variabili: archiviazione dei dati

Le variabili nei flussi di lavoro sono molto simili alle variabili usate in linguaggi imperativi: descrivono una posizione denominata per i dati da archiviare e seguono determinate regole di ambito.  Per creare una variabile, è necessario innanzitutto determinare l'ambito di cui deve essere disponibile la variabile.  Proprio come si possono avere variabili nel codice disponibili a livello di classe o metodo, le variabili del flusso di lavoro possono essere definite in ambiti diversi.  Prendere in considerazione il flusso di lavoro nella figura 5.  In questo esempio è possibile definire una variabile a livello radice del flusso di lavoro o all'ambito definito dall'attività Raccolta dati feed. 

Figura 5: Variabili con ambito attività

Argomenti: passaggio di dati

Gli argomenti vengono definiti sulle attività e definiscono il flusso di dati all'interno e all'esterno dell'attività.  È possibile pensare agli argomenti alle attività in modo molto simile all'uso di argomenti per i metodi nel codice imperativo.  Gli argomenti possono essere In, Out o In/Out e avere un nome e un tipo.  Quando si compila un flusso di lavoro, è possibile definire argomenti sull'attività radice che consente di passare i dati al flusso di lavoro quando viene richiamato.  È possibile definire gli argomenti nel flusso di lavoro in modo che le variabili usino la finestra dell'argomento. 

Quando si aggiungono attività al flusso di lavoro, sarà necessario configurare gli argomenti per le attività e questa operazione viene eseguita principalmente facendo riferimento a variabili nell'ambito o usando espressioni, che verranno illustrate di seguito.  Infatti, la classe base Argument contiene una proprietà Expression che è un'attività che restituisce un valore del tipo di argomento, quindi tutte queste opzioni sono correlate e si basano sull'attività. 

Quando si usa la finestra di progettazione del flusso di lavoro per modificare gli argomenti, è possibile digitare espressioni che rappresentano valori letterali nella griglia delle proprietà oppure usare nomi di variabili per fare riferimento a una variabile nell'ambito, come illustrato nella figura 6, in cui emailResult e emailAddress sono variabili definite nel flusso di lavoro. 

figura 6: Configurazione degli argomenti sulle attività

Espressioni: azione sui dati

Le espressioni sono attività che è possibile usare nel flusso di lavoro per operare sui dati.  Le espressioni possono essere usate in posizioni in cui si usa l'attività e si è interessati a un valore restituito, il che significa che è possibile usare espressioni nell'impostazione di argomenti o per definire condizioni su attività come le attività While o If.  Tenere presente che la maggior parte degli elementi in WF4 deriva da Activity e le espressioni non sono diverse, sono una derivata di Activity<TResult> il che significa che restituiscono un valore di un tipo specifico.  Inoltre, WF4 include diverse espressioni comuni per fare riferimento a variabili e argomenti, nonché espressioni di Visual Basic.  A causa di questo livello di espressioni specializzate, le espressioni usate per definire gli argomenti possono includere codice, valori letterali e riferimenti a variabili.  La tabella 1 fornisce un piccolo campionamento dei tipi di espressioni che è possibile usare per definire gli argomenti tramite la finestra di progettazione del flusso di lavoro.  Quando si creano espressioni nel codice sono disponibili diverse opzioni, tra cui l'uso di espressioni lambda. 

Espressione Tipo di espressione

"hello world"

Valore stringa letterale

10

Valore int32 letterale

System.String.Concat("hello", " ", "world")

Chiamata al metodo imperativo

"hello" & "world"

Espressione di Visual Basic

argInputString

Riferimento all'argomento (nome argomento)

varResult

Riferimento alle variabili (nome variabile)

"hello: " & argInputString

Valori letterali e argomenti/variabili misti

Tabella 1: espressioni di esempio

Creazione del primo flusso di lavoro

Dopo aver trattato i concetti di base relativi all'attività e al flusso di dati, è possibile creare un flusso di lavoro usando questi concetti.  Inizierò con un semplice flusso di lavoro hello world per concentrarsi sui concetti anziché sulla vera proposta di valore di WF.  Per iniziare, creare un nuovo progetto unit test in Visual Studio 2010.  Per usare il nucleo di WF, aggiungere un riferimento all'assembly System.Activities e aggiungere istruzioni using per System.Activities, System.Activities.Statements e System.IO nel file della classe di test.  Aggiungere quindi un metodo di test come illustrato nella figura 7 per creare un flusso di lavoro di base ed eseguirlo. 

[TestMethod]

public void TestHelloWorldStatic()

{

    StringWriter writer = new StringWriter();

    Console.SetOut(writer);

    Sequenza wf = nuova sequenza

    {

        Attività = {

            new WriteLine {Text = "Hello"},

            new WriteLine {Text = "World"}

        }

    };

    WorkflowInvoker.Invoke(wf);

    Assert.IsTrue(String.Compare(

        "Hello\r\nWorld\r\n",

        scrittore. GetStringBuilder(). ToString()) == 0,

        "Stringa non corretta scritta");

}

figura 7: Creazione di hello world nel codice

La proprietà Text dell'attività WriteLine è un oggetto InArgument<stringa> e in questo esempio ho passato un valore letterale a tale proprietà.

Il passaggio successivo consiste nell'aggiornare questo flusso di lavoro per usare le variabili e passare tali variabili agli argomenti dell'attività.  La figura 8 mostra il nuovo test aggiornato per usare le variabili.

[TestMethod]

public void TestHelloWorldVariables()

{

    StringWriter writer = new StringWriter();

    Console.SetOut(writer);

Sequenza wf = nuova sequenza

{

    Variables = {

        new Variable<string>{Default = "Hello", Name = "greeting"},

        new Variable<string> { Default = "Bill", Name = "name" } },

        Attività = {

            new WriteLine { Text = new VisualBasicValue<string>("greeting"),

            new WriteLine { Text = new VisualBasicValue<string>(

            "name + \"Gates\"")}

        }

    };

    WorkflowInvoker.Invoke(wf);

}

Figura 8: Flusso di lavoro di codice con variabili

In questo caso, le variabili vengono definite come tipo Variabile<stringa> e dato un valore predefinito.  Le variabili vengono dichiarate all'interno dell'attività Sequence e quindi vi viene fatto riferimento dall'argomento Text delle due attività.  In alternativa, è possibile usare espressioni per inizializzare le variabili, come illustrato nella figura 9, in cui viene usata la classe<TResult> passando una stringa che rappresenta l'espressione.  Nel primo caso, l'espressione fa riferimento al nome della variabile e nel secondo caso la variabile viene concatenata con un valore letterale.  La sintassi usata nelle espressioni testuali è Visual Basic, anche quando si scrive codice in C#.

Attività = {

    new WriteLine { Text = new VisualBasicValue<string>("greeting"),

        TextWriter = writer },

    new WriteLine { Text = new VisualBasicValue<stringa>("name + \"Gates\""),

        TextWriter = writer }

}

figura 9: Uso di espressioni per definire argomenti

Mentre gli esempi di codice consentono di illustrare punti importanti e in genere si sentono a proprio agio per gli sviluppatori, la maggior parte delle persone userà la finestra di progettazione per creare flussi di lavoro.  Nella finestra di progettazione trascinare le attività nell'area di progettazione e usare una griglia delle proprietà aggiornata ma familiare per impostare gli argomenti.  Inoltre, la finestra di progettazione del flusso di lavoro contiene aree espandibili nella parte inferiore per modificare gli argomenti per il flusso di lavoro e le variabili.   Per creare un flusso di lavoro simile nella finestra di progettazione, aggiungere un nuovo progetto alla soluzione, scegliere il modello Libreria attività e quindi aggiungere una nuova attività. 

Quando la finestra di progettazione del flusso di lavoro viene visualizzata per la prima volta, non contiene alcuna attività.  Il primo passaggio per definire l'attività consiste nel scegliere l'attività radice.  Per questo esempio, aggiungere un'attività Diagramma di flusso alla finestra di progettazione trascinandola dalla categoria Diagramma di flusso nella casella degli strumenti.  Nella finestra di progettazione Diagramma di flusso trascinare due attività WriteLine dalla casella degli strumenti e aggiungerle una sotto l'altra al diagramma di flusso.  È ora necessario connettere le attività insieme in modo che il diagramma di flusso conosca il percorso da seguire.  A tale scopo, passare il puntatore del mouse sul cerchio verde "start" nella parte superiore del diagramma di flusso per visualizzare i quadratini di afferramento, quindi fare clic e trascinarne uno al primo writeLine e rilasciarlo sul quadratino di trascinamento visualizzato nella parte superiore dell'attività.  Eseguire la stessa operazione per connettere la prima proprietà WriteLine al secondo writeline.  L'area di progettazione dovrebbe essere simile alla figura 10. 

figura 10: layout del diagramma di flusso Hello

Dopo aver configurato la struttura, è necessario configurare alcune variabili.  Facendo clic sull'area di progettazione e quindi facendo clic sul pulsante Variabili nella parte inferiore della finestra di progettazione, è possibile modificare la raccolta di variabili per il diagramma di flusso.  Aggiungere due variabili denominate "greeting" e "name" per elencare e impostare rispettivamente i valori predefiniti su "Hello" e "Bill". Assicurarsi di includere le virgolette quando si impostano i valori come espressione, in modo che le stringhe letterali debbano essere racchiuse tra virgolette.  La figura 11 mostra la finestra delle variabili configurata con le due variabili e i relativi valori predefiniti. 

figura 11: Variabili definite nella finestra di progettazione del flusso di lavoro

Per usare queste variabili nelle attività WriteLine, immettere "greeting" (senza virgolette) nella griglia delle proprietà per l'argomento Text della prima attività e "name" (di nuovo senza virgolette) per l'argomento Text nel secondo WriteLine.   In fase di esecuzione, quando vengono valutati gli argomenti Text, il valore nella variabile verrà risolto e usato dall'attività WriteLine.

Nel progetto di test aggiungere un riferimento al progetto contenente il flusso di lavoro.  È quindi possibile aggiungere un metodo di test simile a quello illustrato nella figura 12 per richiamare questo flusso di lavoro e testare l'output.  Nella figura 12 è possibile vedere che il flusso di lavoro viene creato creando un'istanza della classe creata.  In questo caso il flusso di lavoro è stato definito nella finestra di progettazione e compilato in una classe che deriva dall'attività che può quindi essere richiamata. 

[TestMethod]

public void TestHelloFlowChart()

{

    StringWriter tWriter = new StringWriter();

    Console.SetOut(tWriter);

    Workflow.HelloFlow wf = new Workflows.HelloFlow();

    WorkflowInvoker.Invoke(wf);

    Asserzioni omesse

}

figura 12: Test del diagramma di flusso

Finora sono state usate variabili nel flusso di lavoro e sono state usate per fornire valori agli argomenti sulle attività WriteLine.  Il flusso di lavoro può anche avere argomenti definiti che consentono di passare dati al flusso di lavoro quando lo richiamano e di ricevere l'output al termine del flusso di lavoro.  Per aggiornare il diagramma di flusso dell'esempio precedente, rimuovere la variabile "name" (selezionarla e premere canc) e creare invece un argomento "name" di tipo string.  Questa operazione viene eseguita in modo analogo, ad eccezione del pulsante Argomenti per visualizzare l'editor di argomenti.  Si noti che gli argomenti possono avere anche una direzione e non è necessario specificare un valore predefinito perché il valore verrà passato all'attività in fase di esecuzione.  Poiché si usa lo stesso nome per l'argomento come è stato fatto per la variabile, gli argomenti Text delle attività WriteLine rimangono validi.  Ora in fase di esecuzione, questi argomenti valuteranno e risolveranno il valore dell'argomento "name" nel flusso di lavoro e useranno tale valore.  Aggiungere un argomento aggiuntivo di tipo string con la direzione Out e il nome "fullGreeting"; verrà restituito al codice chiamante. 

Figura 13: Definizione di argomenti per il flusso di lavoro

Nel diagramma di flusso aggiungere un'attività Assign e connetterla all'ultima attività WriteLine.  Per l'argomento To immettere "fullGreeting" (senza virgolette) e per l'argomento Valore immettere "greeting & name" (senza virgolette). Verrà assegnata la concatenazione della variabile greeting con l'argomento name all'argomento di output fullGreeting. 

Ora nello unit test aggiornare il codice per specificare l'argomento quando si richiama il flusso di lavoro.  Gli argomenti vengono passati al flusso di lavoro come stringa dictionary<stringa, oggetto> dove la chiave è il nome dell'argomento.  A tale scopo, è sufficiente modificare la chiamata per richiamare il flusso di lavoro, come illustrato nella figura 14. Si noti che gli argomenti di output sono contenuti anche in una stringa dictionary<, oggetto> raccolta con chiave sul nome dell'argomento. 

IDictionary<stringa, oggetto> risultati = WorkflowInvoker.Invoke(wf,

    new Dictionary<string,object> {

        {"name", "Bill" } }

    );

string outValue = results["fullGreeting"]. ToString();

figura 14: Passaggio di argomenti a un flusso di lavoro

Dopo aver visto le nozioni di base su come mettere insieme attività, variabili e argomenti, ti guiderò in una panoramica delle attività incluse nel framework per abilitare flussi di lavoro più interessanti incentrati sulla logica di business invece di concetti di basso livello.

Panoramica del riquadro attività del flusso di lavoro

Con qualsiasi linguaggio di programmazione, si prevede di avere costrutti di base per definire la logica dell'applicazione.  Quando si usa un framework di sviluppo di livello superiore come WF, tale aspettativa non cambia.   Proprio come si dispone di istruzioni in linguaggi .NET come If/Else, Switch e While, per la gestione del flusso di controllo sono necessarie anche queste stesse funzionalità quando si definisce la logica in un flusso di lavoro dichiarativo.  Queste funzionalità sono disponibili sotto forma di libreria di attività di base fornita con il framework.  In questa sezione ti darò un rapido tour delle attività fornite con il framework per dare un'idea delle funzionalità fornite in modo risolto.

Primitive di attività e attività di raccolta

Quando si passa a un modello di programmazione dichiarativa, è facile iniziare a chiedersi come eseguire attività comuni di manipolazione degli oggetti che sono di seconda natura durante la scrittura di codice.  Per queste attività in cui ci si trova a lavorare con gli oggetti e dover impostare le proprietà, richiamare comandi o gestire una raccolta di elementi, è presente un set di attività progettate specificamente con tali attività. 

Attività Descrizione

Assegnare

Assegna un valore a una posizione, abilitando le variabili di impostazione.

Ritardo

Ritarda il percorso di esecuzione per un periodo di tempo specificato.

InvokeMethod

Richiama un metodo su un oggetto .NET o un metodo statico in un tipo .NET, facoltativamente con un tipo restituito T.

WriteLine

Scrive il testo specificato in un writer di testo. L'impostazione predefinita è Console.Out

AddToCollection<T>

Aggiunge un elemento a una raccolta tipizzata.

RemoveFromCollection<T>

Rimuove un elemento da una raccolta tipizzata.

> ClearCollection<T

Rimuove tutti gli elementi da una raccolta.

ExistsInCollection<T>

Restituisce un valore booleano che indica se l'elemento specificato esiste nell'insieme. 

 

Attività del flusso di controllo

Quando si definiscono la logica di business o i processi aziendali, il controllo del flusso di esecuzione è fondamentale. Le attività del flusso di controllo includono nozioni di base, ad esempio Sequenza, che fornisce un contenitore comune quando è necessario eseguire i passaggi in ordine e la logica di diramazione comune, ad esempio le attività If e Switch.  Le attività del flusso di controllo includono anche la logica di ciclo basata sui dati (ForEach) e Condizioni(While).  Più importante per semplificare la programmazione complessa sono le attività parallele, che consentono a tutte più attività asincrone di svolgere il lavoro contemporaneamente. 

Attività Descrizione

Sequenza

Per l'esecuzione di attività in serie

While/DoWhile

Esegue un'attività figlio mentre una condizione (espressione) è true

ForEach<T>

Esegue un'iterazione su una raccolta enumerabile ed esegue l'attività figlio una volta per ogni elemento della raccolta, in attesa del completamento dell'elemento figlio prima di avviare l'iterazione successiva. Fornisce l'accesso tipizzato al singolo elemento che guida l'iterazione sotto forma di argomento denominato.

Se

Esegue una delle due attività figlio a seconda del risultato della condizione (espressione).

Switch<T>

Valuta un'espressione e pianifica l'attività figlio con una chiave corrispondente. 

Parallelo

Pianifica tutte le attività figlio contemporaneamente, ma fornisce anche una condizione di completamento per consentire all'attività di annullare eventuali attività figlio in sospeso se vengono soddisfatte determinate condizioni. 

ParallelForEach<T>

Esegue un'iterazione su una raccolta enumerabile ed esegue l'attività figlio una volta per ogni elemento nella raccolta, pianificando tutte le istanze contemporaneamente. Analogamente a ForEach<T>, questa attività fornisce l'accesso all'elemento di dati corrente sotto forma di argomento denominato.

Scegliere

Pianifica tutte le attività PickBranch figlio e annulla tutte le attività, ma il primo a completare il trigger.  L'attività PickBranch ha sia un trigger che un'azione; ogni è un'attività.  Al termine di un'attività trigger, la selezione annulla tutte le altre attività figlio. 

 I due esempi seguenti illustrano diverse di queste attività in uso per illustrare come comporre queste attività insieme.  Il primo esempio, figura 15, include l'uso del> ParallelForEach<T per usare un elenco di URL e ottenere in modo asincrono il feed RSS all'indirizzo specificato.  Dopo la restituzione del feed, il> ForEach<T viene usato per scorrere gli elementi del feed ed elaborarli. 

Si noti che il codice dichiara e definisce un'istanza di VisualBasicSettings con riferimenti ai tipi System.ServiceModel.Syndication.  Questo oggetto settings viene quindi usato quando si dichiara VisualBasicValue<T> istanze che fanno riferimento a tipi di variabili di tale spazio dei nomi per abilitare la risoluzione dei tipi per tali espressioni.  Si noti anche che il corpo dell'attività ParallelForEach accetta un elemento ActivityAction menzionato nella sezione sulla creazione di attività personalizzate.  Queste azioni usano DelegateInArgument e DelegateOutArgument nello stesso modo in cui le attività usano InArgument e OutArgument. 

Figura 15: Attività del flusso di controllo

Il secondo esempio, figura 16, è un modello comune di attesa di una risposta con un timeout.  Ad esempio, in attesa che un responsabile approvi una richiesta e invii un promemoria se la risposta non è arrivata nel tempo assegnato.  Un'attività DoWhile causa la ripetizione dell'attesa di una risposta mentre l'attività Pick viene usata per eseguire un'attività ManagerResponse e un'attività Delay contemporaneamente ai trigger.  Al termine della prima operazione, viene inviato il promemoria e al termine dell'attività ManagerResponse, il flag viene impostato per interrompere il ciclo DoWhile. 

figura 16: Selezionare e fare le attività

L'esempio nella figura 16 mostra anche come usare i segnalibri nei flussi di lavoro.  Un segnalibro viene creato da un'attività per contrassegnare una posizione nel flusso di lavoro, in modo che l'elaborazione possa riprendere da quel punto in un secondo momento.  L'attività Delay ha un segnalibro che viene ripreso dopo un determinato periodo di tempo.  L'attività ManagerResponse è un'attività personalizzata che attende l'input e riprende il flusso di lavoro dopo l'arrivo dei dati.  Le attività di messaggistica, descritte a breve, sono le attività principali per l'esecuzione di segnalibri.  Quando un flusso di lavoro non elabora attivamente il lavoro, quando è in attesa solo della ripresa dei segnalibri, viene considerato inattiva e può essere salvato in modo permanente in un archivio durevole.  I segnalibri verranno illustrati in modo più dettagliato nella sezione relativa alla creazione di attività personalizzate. 

Migrazione

Per gli sviluppatori che usano WF3, l'attività Di interoperabilità può svolgere un ruolo fondamentale nel riusare gli asset esistenti. L'attività, disponibile nell'assembly System.Workflow.Runtime, esegue il wrapping del tipo di attività esistente e visualizza le proprietà dell'attività come argomenti nel modello WF4.  Poiché le proprietà sono argomenti, è possibile usare espressioni per definire i valori.  La figura 17 mostra la configurazione dell'attività Di interoperabilità per chiamare un'attività WF3.  Gli argomenti di input vengono definiti con riferimenti alle variabili nell'ambito all'interno della definizione del flusso di lavoro.    Le attività compilate in WF3 avevano proprietà anziché argomenti, pertanto a ogni proprietà viene assegnato un argomento di input e output corrispondente che consente di distinguere i dati inviati nell'attività e i dati che si prevede di recuperare dopo l'esecuzione dell'attività. In un nuovo progetto WF4 non è possibile trovare questa attività nella casella degli strumenti perché il framework di destinazione è impostato su .NET Framework 4 Client Profile.  Modificare il framework di destinazione nelle proprietà del progetto in .NET Framework 4 e l'attività verrà visualizzata nella casella degli strumenti.

Figura 17: Configurazione dell'attività di interoperabilità

Organigramma

Quando si progettano flussi di lavoro diagramma di flusso sono disponibili diversi costrutti che possono essere usati per gestire il flusso di esecuzione all'interno del diagramma di flusso.  Questi costrutti forniscono semplici passaggi, semplici punti decisionali in base a una singola condizione o a un'istruzione switch.  La potenza reale del diagramma di flusso è la possibilità di connettere questi tipi di nodo al flusso desiderato.

Costrutto/attività Descrizione

Organigramma

Il contenitore per una serie di passaggi del flusso, ogni passaggio del flusso può essere qualsiasi attività o uno dei costrutti seguenti, ma da eseguire, deve essere connesso all'interno del diagramma di flusso. 

FlowDecision

Fornisce la logica di diramazione basata su una condizione.

FlussoSwitch<T>

Abilita più rami in base al valore di un'espressione. 

FlowStep

Rappresenta un passaggio del diagramma di flusso con la possibilità di essere connessi ad altri passaggi. Questo tipo non viene visualizzato nella casella degli strumenti perché viene aggiunto in modo implicito dalla finestra di progettazione.  Questa attività esegue il wrapping di altre attività nel flusso di lavoro e fornisce la semantica di navigazione per i passaggi successivi nel flusso di lavoro. 

È importante notare che, mentre sono presenti attività specifiche per il modello Diagramma di flusso, è possibile usare qualsiasi altra attività all'interno del flusso di lavoro.  Analogamente, un'attività diagramma di flusso può essere aggiunta a un'altra attività per fornire la semantica di esecuzione e progettazione di un diagramma di flusso all'interno di tale flusso di lavoro.  Ciò significa che è possibile avere una sequenza con diverse attività e un diagramma di flusso direttamente al centro. 

Attività di messaggistica

Uno degli elementi principali di WF4 è un'integrazione più stretta tra WF e WCF.  In termini di flussi di lavoro, ciò significa attività per modellare le operazioni di messaggistica, ad esempio l'invio e la ricezione di messaggi.  Esistono in realtà diverse attività incluse nel framework per la messaggistica, ognuna con funzionalità e finalità leggermente diverse. 

Attività Descrizione

Invio/ricezione

Attività di messaggistica unidirezionale per inviare o ricevere un messaggio.  Queste stesse attività sono composte in interazioni di tipo richiesta/risposta. 

ReceiveAndSendReply

Modella un'operazione del servizio che riceve un messaggio e invia una risposta. 

SendAndReceiveReply

Richiama un'operazione del servizio e riceve la risposta. 

InitializeCorrelation

Consente di inizializzare i valori di correlazione in modo esplicito come parte della logica del flusso di lavoro, anziché estrarre i valori da un messaggio. 

CorrelationScope

Definisce un ambito di esecuzione in cui un handle di correlazione è accessibile per ricevere e inviare attività semplificando la configurazione di un handle condiviso da diverse attività di messaggistica. 

TransactedReceiveScope

Consente di includere la logica del flusso di lavoro nella stessa transazione in un'operazione WCF usando l'attività Receive.

Per richiamare un'operazione del servizio dall'interno di un flusso di lavoro, seguire i passaggi noti dell'aggiunta di un riferimento al servizio al progetto del flusso di lavoro.  Il sistema di progetto in Visual Studio utilizzerà quindi i metadati del servizio e creerà un'attività personalizzata per ogni operazione del servizio trovata nel contratto.  È possibile considerare questo aspetto come l'equivalente del flusso di lavoro di un proxy WCF che verrebbe creato in un progetto C# o Visual Basic.   Ad esempio, prendendo un servizio esistente che cerca prenotazioni di hotel con il contratto di servizio illustrato nella figura 18; aggiungendo un riferimento al servizio, viene restituita un'attività personalizzata illustrata nella figura 19. 

[ServiceContract]

interfaccia pubblica IHotelService

{

    [OperationContract]

    Elencare<HotelSearchResult> SearchElement(

        HotelSearchRequest requestDetails);

}

Figura 18: Contratto di servizio

figura 19: di attività WCF personalizzate

Altre informazioni sulla creazione di flussi di lavoro esposti come servizi WCF sono disponibili nella sezione Servizi flusso di lavoro più avanti in questo documento. 

Transazioni e gestione degli errori

La scrittura di sistemi affidabili può essere difficile e richiede un buon lavoro per gestire gli errori e gestire lo stato coerente nell'applicazione.  Per un flusso di lavoro, gli ambiti per la gestione delle eccezioni e le transazioni vengono modellati usando attività con proprietà di tipo ActivityAction o Activity per consentire agli sviluppatori di modellare la logica di elaborazione degli errori.  Oltre a questi modelli comuni di coerenza, WF4 include anche attività che consentono di modellare il coordinamento distribuito a esecuzione prolungata tramite compensazione e conferma. 

Attività Descrizione

CancellationScope

Usato per consentire allo sviluppatore del flusso di lavoro di reagire se un corpo di lavoro viene annullato. 

TransactionScope

Abilita la semantica simile all'uso di un ambito di transazione nel codice eseguendo tutte le attività figlio nell'ambito in una transazione. 

> TryCatch/Catch<T

Usato per modellare la gestione delle eccezioni e rilevare le eccezioni tipate. 

Gettare

Può essere usato per generare un'eccezione dall'attività.  

Rethrow

Usato per rigenerare un'eccezione, in genere una rilevata usando l'attività TryCatch. 

CompensableActivity

Definisce la logica per l'esecuzione di attività figlio che potrebbero dover compensare le azioni dopo l'esito positivo.  Fornisce un segnaposto per la logica di compensazione, la logica di conferma e la gestione dell'annullamento.

Compensare

Richiama la logica di gestione della compensazione per un'attività compensabile.

Confermare

Richiama la logica di conferma per un'attività compensabile. 

L'attività TryCatch offre un modo familiare per definire l'ambito di un set di lavoro per rilevare eventuali eccezioni che possono verificarsi e l'attività Catch<T> fornisce il contenitore per la logica di gestione delle eccezioni.  È possibile vedere un esempio di come viene usata questa attività nella figura 20, con ogni blocco di eccezioni tipizzato definito da un'attività Catch<T>, fornendo l'accesso all'eccezione tramite l'argomento denominato visualizzato a sinistra di ogni catch.   In caso di necessità, l'attività Rethrow può essere usata per rigenerare l'eccezione rilevata o l'attività Throw per generare una nuova eccezione personalizzata. 

figura 20: di attività TryCatch

Le transazioni consentono di garantire la coerenza nel lavoro di breve durata e l'attività TransactionScope fornisce lo stesso tipo di ambito che è possibile ottenere nel codice .NET usando la classe TransactionScope. 

Infine, quando è necessario mantenere la coerenza, ma non è possibile usare una transazione atomica tra le risorse, è possibile usare la compensazione.  La compensazione consente di definire un set di lavoro che, se completato, può avere una serie di attività per compensare le modifiche apportate.  Come esempio semplice, si consideri l'attività compensabile nella figura 21 in cui viene inviato un messaggio di posta elettronica.  Se dopo il completamento dell'attività, si verifica un'eccezione, la logica di compensazione può essere richiamata per ripristinare lo stato coerente del sistema; in questo caso un messaggio di posta elettronica di completamento per ritirare il messaggio precedente. 

Figura 21: di attività compensabili

Creazione ed esecuzione di flussi di lavoro

Come per qualsiasi linguaggio di programmazione, esistono due operazioni fondamentali eseguite con i flussi di lavoro: definirli ed eseguirli.  In entrambi i casi, WF offre diverse opzioni per offrire flessibilità e controllo. 

Opzioni per la progettazione di flussi di lavoro

Quando si progettano o si definiscono flussi di lavoro, sono disponibili due opzioni principali: codice o XAML. XAML offre l'esperienza veramente dichiarativa e consente di definire l'intera definizione del flusso di lavoro nel markup XML, facendo riferimento ad attività e tipi compilati con .NET.  La maggior parte degli sviluppatori userà probabilmente la finestra di progettazione del flusso di lavoro per creare flussi di lavoro che genereranno una definizione dichiarativa del flusso di lavoro XAML.  Poiché XAML è solo XML, tuttavia, qualsiasi strumento può essere usato per crearlo, che è uno dei motivi per cui è un modello così potente per la creazione di applicazioni.  Ad esempio, il codice XAML illustrato nella figura 22 è stato creato in un semplice editor di testo e può essere compilato o usato direttamente per eseguire un'istanza del flusso di lavoro in fase di definizione. 

<p:Activity x:Class="Workflows.HelloSeq" xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities/design" xmlns:p="https://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">

  <x:Members>

    <x:Property Name="greeting" Type="p:InArgument(x:String)" />

    <x:Property Name="name" Type="p:InArgument(x:String)" />

  </x:Members>

  <p:Sequence>

    <p:WriteLine>[greeting & " from workflow"]</p:WriteLine>

    <p:WriteLine>[name]</p:WriteLine>

  </p:Sequence>

</p:Activity>

figura 22: Flusso di lavoro definito nel XAML

Questo stesso codice XAML viene generato dalla finestra di progettazione e può essere generato da strumenti personalizzati, semplificando la gestione.  Inoltre, è anche possibile, come illustrato in precedenza, per compilare flussi di lavoro usando il codice.  Ciò implica la creazione della gerarchia di attività tramite l'uso delle varie attività nel framework e delle attività personalizzate.  Sono stati visualizzati diversi esempi già di flussi di lavoro definiti interamente nel codice.  A differenza di WF3, è ora possibile creare un flusso di lavoro nel codice e serializzarlo facilmente in XAML; offrendo maggiore flessibilità nella modellazione e nella gestione delle definizioni del flusso di lavoro. 

Come mostrerò, per eseguire un flusso di lavoro, tutto ciò di cui hai bisogno è un'attività e che può essere un'istanza incorporata nel codice o una creata da XAML.  Il risultato finale di ognuna delle tecniche di modellazione è una classe derivata da Activity o una rappresentazione XML che può essere deserializzata o compilata in un'attività. 

Opzioni per l'esecuzione dei flussi di lavoro

Per eseguire un flusso di lavoro, è necessaria un'attività che definisce il flusso di lavoro.  Esistono due modi tipici per ottenere un'attività che può essere eseguita: crearla nel codice o leggerla in un file XAML e deserializzare il contenuto in un'attività.  La prima opzione è semplice e ho già mostrato diversi esempi.  Per caricare un file XAML, è necessario usare la classe ActivityXamlServices che fornisce un metodo Load statico.  È sufficiente passare un oggetto Stream o XamlReader e si ottiene di nuovo l'attività rappresentata nel codice XAML.  

Dopo aver creato un'attività, il modo più semplice per eseguirlo consiste nell'usare la classe WorkflowInvoker come ho fatto negli unit test precedenti.  Il metodo Invoke di questa classe ha un parametro di tipo Activity e restituisce un oggetto IDictionary<stringa, oggetto>.  Se è necessario passare argomenti nel flusso di lavoro, definirli prima nel flusso di lavoro e quindi passare i valori insieme all'attività, nel metodo Invoke come dizionario di coppie nome/valore.  Analogamente, tutti gli argomenti Out o In/Out definiti nel flusso di lavoro verranno restituiti come risultato dell'esecuzione del metodo.  La figura 23 fornisce un esempio di caricamento di un flusso di lavoro da un file XAML, il passaggio di argomenti nel flusso di lavoro e il recupero degli argomenti di output risultanti. 

Activity mathWF;

using (Stream mathXaml = File.OpenRead("Math.xaml"))

{

    mathWF = ActivityXamlServices.Load(mathXaml);

}

var outputs = WorkflowInvoker.Invoke(mathWF,

    new Dictionary<string, object> {

    { "operando1", 5 },

    { "operando2", 10 },

    { "operation", "add" } });

Assert.AreEqual<int>(15, (int)outputs["result"], "Risultato non corretto restituito");

figura 23: richiamo del flusso di lavoro "XAML libero" con argomenti in e out

Si noti che in questo esempio l'attività viene caricata da un file XAML e può comunque accettare e restituire argomenti.  Il flusso di lavoro è stato sviluppato usando la finestra di progettazione in Visual Studio per semplificare, ma può essere sviluppato in una finestra di progettazione personalizzata e archiviato ovunque.  Il codice XAML può essere caricato da un database, da una raccolta di SharePoint o da un altro archivio prima di essere consegnato al runtime per l'esecuzione.  

L'uso di WorkflowInvoker fornisce il meccanismo più semplice per l'esecuzione di flussi di lavoro di breve durata.  Ciò rende essenzialmente il flusso di lavoro come una chiamata di metodo nell'applicazione, consentendo di sfruttare più facilmente tutti i vantaggi di WF senza dover eseguire molte operazioni per ospitare WF stesso.  Ciò è particolarmente utile quando si testano unità le attività e i flussi di lavoro in quanto riduce la configurazione di test necessaria per eseguire un componente sottoposto a test. 

Un'altra classe di hosting comune è WorkflowApplication che fornisce un handle sicuro a un flusso di lavoro in esecuzione nel runtime e consente di gestire più facilmente i flussi di lavoro a esecuzione prolungata.  Con WorkflowApplication è comunque possibile passare argomenti nel flusso di lavoro nello stesso modo di WorkflowInvoker, ma si usa il metodo Run per avviare effettivamente il flusso di lavoro in esecuzione.  A questo punto, il flusso di lavoro inizia l'esecuzione su un altro thread e il controllo torna al codice chiamante. 

Poiché il flusso di lavoro è ora in esecuzione in modo asincrono, nel codice di hosting è probabile che si voglia sapere quando il flusso di lavoro viene completato o se genera un'eccezione e così via. Per questi tipi di notifiche, la classe WorkflowApplication ha un set di proprietà di tipo Action<T> che possono essere usate come eventi per aggiungere codice per reagire a determinate condizioni dell'esecuzione del flusso di lavoro, tra cui: eccezione interrotta, non gestita, completata, inattiva e scaricata.  Quando si esegue un flusso di lavoro usando WorkflowApplication, è possibile usare codice simile a quello illustrato nella figura 24 usando azioni per gestire gli eventi. 

WorkflowApplication wf = new WorkflowApplication(new Flowchart1());

Wf. Completed = delegate(WorkflowApplicationCompletedEventArgs e)

{

    Console.WriteLine("Workflow {0} complete", e.InstanceId);

};

Wf. Aborted = delegate(WorkflowApplicationAbortedEventArgs e)

{

    Console.WriteLine(e.Reason);

};

Wf. OnUnhandledException =

  delegate(WorkflowApplicationUnhandledExceptionEventArgs e)

  {

      Console.WriteLine(e.UnhandledException.ToString());

      return UnhandledExceptionAction.Terminate;

  };

Wf. Run();

figura 24: Azioni di applicazione del flusso di lavoro

In questo esempio, l'obiettivo del codice host, una semplice applicazione console, consiste nel notificare all'utente tramite la console quando il flusso di lavoro viene completato o se si verifica un errore.  In un sistema reale, l'host sarà interessato a questi eventi e probabilmente risponderà a tali eventi in modo diverso per fornire agli amministratori informazioni sugli errori o per gestire meglio le istanze.

Estensioni del flusso di lavoro

Una delle funzionalità di base di WF, dal momento che WF3, è stata sufficientemente leggera da essere ospitata in qualsiasi dominio applicazione .NET.  Poiché il runtime può essere eseguito in domini diversi e potrebbe essere necessaria una semantica di esecuzione personalizzata, diversi aspetti dei comportamenti di runtime devono essere esternalizzati dal runtime.  È qui che vengono in gioco le estensioni del flusso di lavoro.  Le estensioni del flusso di lavoro consentono allo sviluppatore di scrivere il codice host, se lo si sceglie, di aggiungere il comportamento al runtime estendendolo con codice personalizzato. 

Due tipi di estensione di cui il runtime WF è a conoscenza sono le estensioni di persistenza e rilevamento. L'estensione di persistenza fornisce la funzionalità di base per salvare lo stato di un flusso di lavoro in un archivio durevole e recuperarlo quando necessario.  La distribuzione dell'estensione di persistenza con il framework include il supporto per Microsoft SQL Server, ma le estensioni possono essere scritte per supportare altri sistemi di database o archivi durevoli. 

Persistenza

Per usare l'estensione di persistenza, è prima necessario configurare un database per contenere lo stato, che può essere eseguito usando gli script SQL disponibili in %windir%\Microsoft.NET\Framework\v4.0.30319\sql\<lingua> (ad esempio c:\windows\microsoft.net\framework\v4.0.30319\sql\en\). Dopo aver creato un database, eseguire i due script per creare sia la struttura (SqlWorkflowInstanceStoreSchema.sql) che le stored procedure (SqlWorkflowInstanceStoreLogic.sql) all'interno del database. 

Dopo aver configurato il database, usare SqlWorkflowInstanceStore insieme alla classe WorkflowApplication.  Creare prima di tutto l'archivio e configurarlo, quindi fornirlo all'oggetto WorkflowApplication, come illustrato nella figura 25.

Applicazione WorkflowApplication = new WorkflowApplication(activity);

InstanceStore instanceStore = new SqlWorkflowInstanceStore(

    @"Data Source=.\\SQLEXPRESS;Integrated Security=True");

Visualizzazione InstanceView = instanceStore.Execute(

    instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(),

    TimeSpan.FromSeconds(30));

instanceStore.DefaultInstanceOwner = view. InstanceOwner;

applicazione. InstanceStore = instanceStore;

figura 25: Aggiunta del provider di persistenza SQL

Esistono due modi in cui il flusso di lavoro può essere salvato in modo permanente.  Il primo consiste nell'uso diretto dell'attività Persist in un flusso di lavoro.  Quando questa attività viene eseguita, lo stato del flusso di lavoro viene salvato in modo permanente nel database, salvando lo stato corrente del flusso di lavoro.  In questo modo, l'autore del flusso di lavoro controlla quando è importante salvare lo stato corrente del flusso di lavoro.  La seconda opzione consente all'applicazione di hosting di rendere persistente lo stato del flusso di lavoro quando si verificano vari eventi nell'istanza del flusso di lavoro; molto probabilmente quando il flusso di lavoro è inattiva.  Registrando per l'azione PersistableIdle in WorkflowApplication, il codice host può quindi rispondere all'evento per indicare se l'istanza deve essere persistente, scaricata o nessuna delle due.  La figura 26 mostra una registrazione dell'applicazione host per ricevere una notifica quando il flusso di lavoro è inattivo e ne causa la persistenza. 

Wf. PersistableIdle = (waie) => PersistableIdleAction.Persist;

Figura 26: Scaricare il flusso di lavoro quando inattivo

Dopo che un flusso di lavoro è stato inattiva e salvato in modo permanente, il runtime di WF può scaricarlo dalla memoria, liberando risorse per altri flussi di lavoro che richiedono l'elaborazione.  È possibile scegliere di scaricare l'istanza del flusso di lavoro restituendo l'enumerazione appropriata dall'azione PersistableIdle.  Una volta scaricato un flusso di lavoro, a un certo punto l'host vuole caricarlo di nuovo dall'archivio di persistenza per riprenderlo.  A tale scopo, l'istanza del flusso di lavoro deve essere caricata usando un archivio di istanze e l'identificatore dell'istanza. A sua volta, l'archivio di istanze viene richiesto di caricare lo stato.  Dopo il caricamento dello stato, è possibile usare il metodo Run in WorkflowApplication oppure riprendere un segnalibro come illustrato nella figura 27.  Per altre informazioni sui segnalibri, vedere la sezione attività personalizzate. 

Applicazione WorkflowApplication = new WorkflowApplication(activity);

applicazione. InstanceStore = instanceStore;

applicazione. Load(id);

applicazione. ResumeBookmark(readLineBookmark, input);

figura 27: Ripresa di un flusso di lavoro persistente

Una delle modifiche apportate a WF4 è il modo in cui lo stato dei flussi di lavoro viene mantenuto e si basa principalmente sulle nuove tecniche di gestione dei dati di argomenti e variabili.  Anziché serializzare l'intero albero delle attività e mantenere lo stato per la durata del flusso di lavoro, solo nelle variabili di ambito e nei valori degli argomenti, insieme ad alcuni dati di runtime, ad esempio le informazioni sui segnalibri, vengono mantenuti.  Si noti nella figura 27 che quando l'istanza persistente viene ricaricata, oltre all'uso dell'archivio di istanze, viene passata anche l'attività che definisce il flusso di lavoro. Essenzialmente, lo stato del database viene applicato all'attività specificata.  Questa tecnica consente una maggiore flessibilità per il controllo delle versioni e offre prestazioni migliori perché lo stato ha un footprint di memoria inferiore. 

Inseguimento

La persistenza consente all'host di supportare processi a esecuzione prolungata, bilanciare il carico delle istanze tra host e altri comportamenti a tolleranza di errore.  Tuttavia, una volta completata l'istanza del flusso di lavoro, lo stato nel database viene spesso eliminato perché non è più utile.  In un ambiente di produzione, avere informazioni sulle operazioni attualmente eseguite da un flusso di lavoro e sulle operazioni eseguite è fondamentale sia per la gestione dei flussi di lavoro che per ottenere informazioni dettagliate sul processo aziendale.  La possibilità di tenere traccia di ciò che accade nell'applicazione è una delle funzionalità interessanti dell'uso del runtime di WF.  

Il rilevamento è costituito da due componenti principali: i partecipanti di rilevamento e i profili di rilevamento.  Un profilo di rilevamento definisce gli eventi e i dati da tenere traccia del runtime. I profili possono includere tre tipi principali di query:

  • ActivityStateQuery: usato per identificare gli stati dell'attività (ad esempio chiusi) e variabili o argomenti per estrarre i dati
  • WorkflowInstanceQuery: usato per identificare gli eventi del flusso di lavoro
  • CustomTrackingQuery: usato per identificare chiamate esplicite per tenere traccia dei dati, in genere all'interno di attività personalizzate

Ad esempio, nella figura 28 viene illustrato un TrackingProfile creato che include ActivityStateQuery e WorkflowInstanceQuery.   Si noti che le query indicano sia quando raccogliere informazioni che i dati da raccogliere.  Per ActivityStateQuery è stato incluso un elenco di variabili che devono avere il valore estratto e aggiunto ai dati rilevati. 

Profilo TrackingProfile = nuovo TrackingProfile

{

    Name = "SimpleProfile",

    Query = {

        new WorkflowInstanceQuery {

            States = { "*" }

        },

        new ActivityStateQuery {

            ActivityName = "WriteLine",

            States={ "*" },

            Variables = {"Text" }

        }

    }

};

Figura 28: Creazione di un profilo di rilevamento

Un partecipante al rilevamento è un'estensione che può essere aggiunta al runtime ed è responsabile dell'elaborazione dei record di rilevamento durante l'emissione.  La classe di base TrackingParticipant definisce una proprietà per fornire un TrackingProfile e un metodo Track che gestisce il rilevamento.  La figura 29 mostra un partecipante di rilevamento personalizzato limitato che scrive i dati nella console.  Per usare il partecipante di rilevamento, deve essere inizializzato con un profilo di rilevamento e quindi aggiunto nella raccolta di estensioni nell'istanza del flusso di lavoro. 

public class ConsoleTrackingParticipant : TrackingParticipant

{

   protected override void Track(TrackingRecord record, timeout TimeSpan)

   {

      ActivityStateRecord aRecord = record come ActivityStateRecord;

      if (aRecord != null)

      {

         Console.WriteLine("{0} immesso lo stato {1}",

            aRecord.Activity.Name, aRecord.State);

         foreach (elemento var in aRecord.Arguments)

         {

            Console.ForegroundColor = ConsoleColor.Cyan;

            Console.WriteLine("Variabile:{0} ha valore: {1}",

                articolo. Chiave, elemento. Valore);

            Console.ResetColor();

         }

      }

   }

}

Figura 29: partecipante al rilevamento della console

WF viene fornito con un EtwTrackingParticipant (ETW = Enterprise Trace for Windows) che è possibile aggiungere al runtime per abilitare il rilevamento ad alte prestazioni dei dati.  ETW è un sistema di traccia che è un componente nativo in Windows e viene usato da molti componenti e servizi nel sistema operativo, inclusi i driver e altro codice a livello di kernel.  I dati scritti in ETW possono essere utilizzati usando codice personalizzato o usando strumenti come l'imminente AppFabric di Windows Server.  Per gli utenti che eseguono la migrazione da WF3, si tratta di una modifica perché non ci sarà la spedizione dei partecipanti al rilevamento SQL come parte del framework.  Tuttavia, Windows Server AppFabric verrà fornito con i consumer ETW che raccoglieranno i dati ETW e lo archivieranno in un database SQL.  Analogamente, è possibile creare un consumer ETW e archiviare i dati in qualsiasi formato preferito o scrivere un partecipante di rilevamento personalizzato, a seconda del modo più appropriato per l'ambiente. 

Creazione di attività personalizzate

Anche se la libreria di attività di base include una ricca tavolozza di attività per interagire con servizi, oggetti e raccolte, non fornisce attività per interagire con sottosistemi quali database, server di posta elettronica o oggetti e sistemi di dominio personalizzati.  Parte dell'introduzione a WF4 sarà capire quali attività di base potrebbero essere necessarie o desiderate durante la creazione di flussi di lavoro.  La cosa importante è che quando si creano unità di base di lavoro, è possibile combinarle in attività più grossolane che gli sviluppatori possono usare nei flussi di lavoro. 

Gerarchia delle classi di attività

Anche se un'attività è un'attività, non è vero che tutte le attività hanno gli stessi requisiti o esigenze.  Per questo motivo, anziché tutte le attività che derivano direttamente dall'attività, esiste una gerarchia di classi di base di attività, illustrata nella figura 30, tra cui è possibile scegliere durante la compilazione di un'attività personalizzata. 

Figura 30: Gerarchia di classi di attività

Per la maggior parte delle attività personalizzate, è possibile derivare da AsyncCodeActivity, CodeActivity o NativeActivity (o una delle varianti generiche) o modellare l'attività in modo dichiarativo.  A livello generale, le quattro classi di base possono essere descritte come segue:

  • Attività: usata per modellare le attività componendo altre attività, in genere definite usando XAML.
  • CodeActivity: una classe base semplificata quando è necessario scrivere codice per svolgere il lavoro.
  • AsyncCodeActivity: usato quando l'attività esegue alcune operazioni in modo asincrono.
  • NativeActivity: quando l'attività deve accedere agli elementi interni del runtime, ad esempio per pianificare altre attività o creare segnalibri.

Nelle sezioni seguenti si creeranno diverse attività per vedere come usare ognuna di queste classi di base.  In generale, come si pensa alla classe base da usare, è consigliabile iniziare con la classe base Activity e verificare se è possibile compilare l'attività usando la logica dichiarativa, come illustrato nella sezione successiva.  Successivamente, CodeActivity fornisce un modello semplificato se si determina che è necessario scrivere codice .NET per eseguire l'attività e AsyncCodeActivity se si vuole che l'attività venga eseguita in modo asincrono.  Infine, se si scrivono attività del flusso di controllo come quelle presenti nel framework (ad esempio, While, Switch, If), sarà necessario derivare dalla classe di base NativeActivity per gestire le attività figlio. 

Composizione di attività tramite ActivityDesigner

Quando si crea un nuovo progetto della libreria di attività o quando si aggiunge un nuovo elemento a un progetto WF e si seleziona il modello Attività, si ottiene un file XAML con un elemento Activity vuoto.  Nella finestra di progettazione, questo si presenta come un'area di progettazione in cui è possibile creare il corpo dell'attività.  Per iniziare a usare un'attività che avrà più di un passaggio, in genere consente di trascinare un'attività Sequence in come Corpo e quindi popolare tale attività con la logica di attività effettiva come corpo rappresenta una singola attività figlio. 

È possibile pensare all'attività in modo molto simile a un metodo su un componente con argomenti.  Nell'attività stessa è possibile definire argomenti, insieme alla relativa direzionalità, per definire l'interfaccia dell'attività.  Le variabili che si desidera usare all'interno dell'attività dovranno essere definite nelle attività che costituiscono il corpo, ad esempio la sequenza radice menzionata in precedenza.  Ad esempio, è possibile compilare un'attività NotifyManager che compone due attività più semplici: GetManager e SendMail. 

Creare prima di tutto un nuovo progetto ActivityLibrary in Visual Studio 2010 e rinominare il file Activity1.xaml in NotifyManager.xaml.  Trascinare quindi un'attività Sequence dalla casella degli strumenti e aggiungerla alla finestra di progettazione.  Dopo aver inserito la sequenza, è possibile popolarla con le attività figlio, come illustrato nella figura 31. Si noti che le attività usate in questo esempio sono attività personalizzate in un progetto a cui si fa riferimento e non sono disponibili nel framework.

figura 31: Notificare le attività del manager

Questa attività deve accettare argomenti per il nome del dipendente e il corpo del messaggio. L'attività GetManager cerca il responsabile del dipendente e invia il messaggio di posta elettronica come stringa OutArgument<stringa>.  Infine, l'attività SendMail invia un messaggio al manager.  Il passaggio successivo consiste nel definire gli argomenti per l'attività espandendo la finestra Argomenti nella parte inferiore della finestra di progettazione e immettendo le informazioni per i due argomenti di input necessari. 

Per comporre questi elementi, è necessario essere in grado di passare gli argomenti di input specificati nell'attività NotifyManager alle singole attività figlio.  Per l'attività GetManager, è necessario il nome del dipendente che è un argomento nell'attività. È possibile usare il nome dell'argomento nella finestra di dialogo delle proprietà per l'argomento employeeName nell'attività GetManager, come illustrato nella figura 31.  L'attività SendMail richiede l'indirizzo di posta elettronica del manager e il messaggio.  Per il messaggio, è possibile immettere il nome dell'argomento contenente il messaggio.  Tuttavia, per l'indirizzo di posta elettronica, è necessario un modo per passare l'argomento out dall'attività GetManager all'argomento nell'argomento per l'attività SendMail.  Per questo è necessaria una variabile. 

Dopo aver evidenziato l'attività Sequence, è possibile usare la finestra Variabili per definire una variabile denominata "mgrEmail".  A questo punto è possibile immettere il nome di tale variabile per l'argomento ManagerEmail out nell'attività GetManager e l'argomento A nell'attività SendMail.  Quando viene eseguita l'attività GetManager, i dati di output verranno archiviati in tale variabile e, quando viene eseguita l'attività SendMail, leggerà i dati da tale variabile come nell'argomento . 

L'approccio appena descritto è un modello puramente dichiarativo per definire il corpo dell'attività.  In alcune circostanze, potrebbe essere preferibile specificare il corpo dell'attività nel codice.  Ad esempio, l'attività può includere una raccolta di proprietà che a sua volta determinano un set di attività figlio; Un set di valori denominati che determinano la creazione di un set di attività Assign è un caso in cui è preferibile usare il codice.  In questi casi, è possibile scrivere una classe che deriva dall'attività e scrivere codice nella proprietà Implementation per creare un'attività (e qualsiasi elemento figlio) per rappresentare la funzionalità dell'attività.  Il risultato finale è lo stesso in entrambi i casi: la logica è definita componendo altre attività, ma solo il meccanismo in base al quale il corpo è definito è diverso.  La figura 32 mostra la stessa attività NotifyManager definita nel codice. 

classe public NotifyManager : Activity

{

    public InArgument<stringa> EmployeeName { get; set; }

    public InArgument<stringa> Message { get; set; }

    Implementazione><func protected override

    {

        Ottieni

        {

            return () =>

            {

                Variabile<stringa> mgrEmail =

                new Variable<string> { Name = "mgrEmail" };

                Sequenza s = nuova sequenza

                {

                    Variables = { mgrEmail },

                    Attività = {

                        new GetManager {

                            EmployeeName =

                                new VisualBasicValue<stringa>("EmployeeName"),

                                Result = mgrEmail,

                        },

                        new SendMail {

                            ToAddress = mgrEmail,

                            MailBody = new VisualBasicValue<stringa>("Message"),

                            From = "test@contoso.com",

                            Subject = "Automated email" (Posta elettronica automatizzata)

                        }

                    }

                };

                restituiscono s;

            };

        }

        set { base. Implementazione = valore; }

    }

}

Figura 32: Composizione delle attività con codice

Si noti che la classe base è Activity e la proprietà Implementation è un func<Activity> per restituire una singola attività. Questo codice è molto simile a quello illustrato in precedenza durante la creazione di flussi di lavoro e questo non dovrebbe sorprendere perché l'obiettivo in entrambi i casi è creare un'attività.  Inoltre, negli argomenti dell'approccio di codice è possibile impostare con le variabili oppure è possibile usare espressioni per connettere un argomento a un altro, come illustrato per gli argomenti EmployeeName e Message man mano che vengono usati nelle due attività figlio. 

Scrittura di classi di attività personalizzate

Se si determina che la logica dell'attività non può essere eseguita componendo altre attività, è possibile scrivere un'attività basata su codice.  Quando si scrivono attività nel codice, si derivano dalla classe appropriata, si definiscono gli argomenti e quindi si esegue l'override del metodo Execute.  Il metodo Execute viene chiamato dal runtime quando è il momento per l'attività di svolgere il proprio lavoro.

Per compilare l'attività SendMail usata nell'esempio precedente, è prima necessario scegliere il tipo di base.  Sebbene sia possibile creare la funzionalità dell'attività SendMail usando la classe base Activity e comporre TryCatch<T> e le attività InvokeMethod, per la maggior parte degli sviluppatori sarà più naturale scegliere tra le classi di base CodeActivity e NativeActivity e scrivere codice per la logica di esecuzione.  Poiché questa attività non deve creare segnalibri o pianificare altre attività, è possibile derivare dalla classe di base CodeActivity.  Inoltre, poiché questa attività restituirà un singolo argomento di output, deve derivare da CodeActivity<> TResult che fornisce un outArgument<TResult>.  Il primo passaggio consiste nel definire diversi argomenti di input per le proprietà di posta elettronica.  Eseguire quindi l'override del metodo Execute per implementare la funzionalità dell'attività.  La figura 33 mostra la classe di attività completata. 

public class SendMail : CodeActivity<SmtpStatusCode>

{

    public InArgument<stringa> To { get; set; }

    public InArgument<stringa> From { get; set; }

    public InArgument<stringa> Subject { get; set; }

    public InArgument<stringa> Corpo { get; set; }

    protected override SmtpStatusCode Execute(

    Contesto CodeActivityContext)

    {

        provare

        {

            SmtpClient client = new SmtpClient();

            cliente. Send(

            From.Get(context), ToAddress.Get(context),

            Subject.Get(context), MailBody.Get(context));

        }

        catch (smtpException smtpEx)

        {

            restituisce smtpEx.StatusCode;

        }

        restituisce SmtpStatusCode.Ok;

    }

}

figura 33: di attività personalizzata SendMail

Esistono diversi aspetti da notare sull'uso degli argomenti.  Sono state dichiarate diverse proprietà .NET standard usando i tipi InArgument<T>.  Questo è il modo in cui un'attività basata su codice definisce i relativi argomenti di input e output.  Tuttavia, all'interno del codice, non posso semplicemente fare riferimento a queste proprietà per ottenere il valore dell'argomento, devo usare l'argomento come tipo di handle per recuperare il valore usando il ActivityContext fornito; in questo caso un Oggetto CodeActivityContext.  Utilizzare il metodo Get della classe argument per recuperare il valore e il metodo Set per assegnare un valore a un argomento. 

Una volta completata l'attività, il metodo Execute viene rilevato dal runtime e presuppone che l'attività venga eseguita e la chiuda.  Per il lavoro asincrono o a esecuzione prolungata, esistono modi per modificare questo comportamento, spiegati in una sezione futura, ma in questo caso, una volta inviato il messaggio di posta elettronica, il lavoro è completato. 

Esaminiamo ora un'attività usando la classe di base NativeActivity per gestire le attività figlio.  Verrà compilata un'attività iteratore che esegue un'attività figlio un determinato numero di volte.  Prima di tutto, è necessario un argomento per contenere il numero di iterazioni da eseguire, una proprietà per l'attività da eseguire e una variabile per contenere il numero corrente di iterazioni. Il metodo Execute chiama un metodo BeginIteration per avviare l'iterazione iniziale e le iterazioni successive vengono richiamate nello stesso modo.  Si noti nella figura 34 che le variabili vengono modificate allo stesso modo degli argomenti usando il metodo Get e Set. 

public class Iterator : NativeActivity

{

    public Activity Body { get; set; }

    public InArgument<int> RequestedIterations { get; set; }

    public Variable<int> CurrentIteration { get; set; }

    public Iterator()

    {

        CurrentIteration = new Variable<int> { Default = 0 };

    }

    protected override void Execute(NativeActivityContext context)

    {

        BeginIteration(context);

    }

    private void BeginIteration(NativeActivityContext context)

    {

        if (RequestedIterations.Get(context) > CurrentIteration.Get(context))

        {

            contesto. ScheduleActivity(Body,

            new CompletionCallback(ChildComplete),

            new FaultCallback(ChildFaulted));

        }

    }

}

figura 34: Attività nativa iteratore

Se si esamina attentamente il metodo BeginIteration, si noterà la chiamata al metodo ScheduleActivity.  Questo è il modo in cui un'attività può interagire con il runtime per pianificare un'altra attività per l'esecuzione ed è perché è necessaria questa funzionalità derivata da NativeActivity.  Si noti anche che, quando si chiama il metodo ScheduleActivity in ActivityExecutionContext, vengono forniti due metodi di callback.  Poiché non si sa quale attività verrà usata come corpo o quanto tempo ci vorrà per completare e poiché WF è un ambiente di programmazione fortemente asincrono, i callback vengono usati per notificare all'attività quando un'attività figlio è stata completata, consentendo di scrivere codice per reagire.

Il secondo callback è un FaultCallback che verrà richiamato se l'attività figlio viene generata un'eccezione.  In questo callback si riceve l'eccezione e si ha la possibilità, tramite ActivityFaultContext, di indicare al runtime che l'errore è stato gestito, che lo impedisce di bubbling e fuori dall'attività. La figura 35 mostra i metodi di callback per l'attività Iterator in cui sia pianificare l'iterazione successiva che FaultCallback gestisce l'errore per consentire all'esecuzione di continuare con l'iterazione successiva.

private void ChildComplete(NativeActivityContext context,

Istanza activityInstance)

{

    CurrentIteration.Set(context, CurrentIteration.Get(context) + 1);

    BeginIteration(context);

}

private void ChildFaulted(NativeActivityFaultContext context, Exception ex,

Istanza activityInstance)

{

    CurrentIteration.Set(context, CurrentIteration.Get(context) + 1);

    contesto. HandleFault();

}

figura 35: callback delle attività

Quando l'attività deve eseguire operazioni a esecuzione prolungata, idealmente il lavoro potrebbe essere passato a un altro thread per consentire l'uso del thread del flusso di lavoro per continuare l'elaborazione di altre attività o per elaborare altri flussi di lavoro.  Tuttavia, è sufficiente avviare i thread e restituire il controllo al runtime può causare problemi perché il runtime non monitora i thread creati.  Se il runtime ha determinato che il flusso di lavoro è inattivo, il flusso di lavoro potrebbe scaricare, eliminare i metodi di callback oppure il runtime potrebbe presupporre che l'attività venga eseguita e passare all'attività successiva nel flusso di lavoro.  Per supportare la programmazione asincrona nell'attività derivata da AsyncCodeActivity o AsyncCodeActivity<T>. 

La figura 36 mostra un esempio di attività asincrona che carica un feed RSS.  Questa firma del metodo execute è diversa per le attività asincrone in quanto restituisce un IAsyncResult.  Scrivere il codice nel metodo execute per avviare l'operazione asincrona.  Eseguire l'override del metodo EndExecute per gestire il callback al termine dell'operazione asincrona e restituire il risultato dell'esecuzione. 

public sealed class GetFeed : AsyncCodeActivity<SyndicationFeed>

{

    public InArgument<Uri> FeedUrl { get; set; }

    protected override IAsyncResult BeginExecute(

    Contesto AsyncCodeActivityContext, callback AsyncCallback,

    stato dell'oggetto)

    {

        var req = (HttpWebRequest)HttpWebRequest.Create(

        FeedUrl.Get(context));

        Req. Metodo = "GET";

        contesto. UserState = req;

        restituisce req. BeginGetResponse(new AsyncCallback(callback), state);

    }

    protected override SyndicationFeed EndExecute(

    Contesto AsyncCodeActivityContext, risultato IAsyncResult)

    {

        HttpWebRequest req = context. UserState come HttpWebRequest;

        WebResponse wr = req. EndGetResponse(result);

        SyndicationFeed localFeed = SyndicationFeed.Load(

        XmlReader.Create(wr. GetResponseStream()));

        restituisce localFeed;

    }

}

figura 36: Creazione di attività asincrone

Concetti aggiuntivi relativi alle attività

Dopo aver visto le nozioni di base per la creazione di attività usando le classi di base, gli argomenti e le variabili e la gestione dell'esecuzione; Tocchi brevemente alcune funzionalità più avanzate che puoi usare nello sviluppo di attività. 

I segnalibri consentono a un autore di attività di creare un punto di ripresa nell'esecuzione di un flusso di lavoro.  Dopo aver creato un segnalibro, può essere ripreso per continuare l'elaborazione del flusso di lavoro da dove è stata interrotta.  I segnalibri vengono salvati come parte dello stato, quindi, a differenza delle attività asincrone, dopo la creazione di un segnalibro, non è un problema per rendere persistente e scaricata l'istanza del flusso di lavoro.  Quando l'host vuole riprendere il flusso di lavoro, carica l'istanza del flusso di lavoro e chiama il metodo ResumeBookmark per riprendere l'istanza da cui è stata interrotta.  Durante la ripresa dei segnalibri, i dati possono essere passati anche all'attività.   La figura 37 mostra un'attività ReadLine che crea un segnalibro per ricevere l'input e registra un metodo di callback da richiamare all'arrivo dei dati.  Il runtime sa quando un'attività ha segnalibri in sospeso e non chiuderà l'attività fino a quando il segnalibro non viene ripreso.  Il metodo ResumeBookmark può essere utilizzato nella classe WorkflowApplication per inviare dati al segnalibro denominato e segnalare BookmarkCallback.  

public class ReadLine : NativeActivity<stringa>

{

    public OutArgument<stringa> InputText { get; set; }

    protected override void Execute(NativeActivityContext context)

    {

        contesto. CreateBookmark("ReadLine",

        new BookmarkCallback(BookmarkResumed));

    }

    private void BookmarkResumed(NativeActivityContext context,

        Segnalibro bk, stato oggetto)

    {

        Result.Set(context, state);

    }

}

figura 37: Creazione di un segnalibro

Un'altra funzionalità potente per gli autori di attività è il concetto di ActivityAction.  ActivityAction è l'equivalente del flusso di lavoro della classe Action nel codice imperativo: descrizione di un delegato comune; ma per alcuni, l'idea di un modello può essere più facile da comprendere.  Si consideri l'attività di> ForEach<T che consente di eseguire l'iterazione su un set di dati e pianificare un'attività figlio per ogni elemento di dati.  È necessario un modo per consentire al consumer dell'attività di definire il corpo ed essere in grado di utilizzare l'elemento dati di tipo T. Essenzialmente, è necessaria un'attività che possa accettare un elemento di tipo T e agire su di esso, ActivityAction<T> viene usato per abilitare questo scenario e fornisce un riferimento all'argomento e alla definizione di un'attività per elaborare l'elemento.  È possibile usare ActivityAction nelle attività personalizzate per consentire ai consumer dell'attività di aggiungere i propri passaggi nei punti appropriati.  In questo modo è possibile creare modelli di flusso di lavoro o attività di un ordinamento, in cui un consumer può compilare le parti pertinenti per personalizzare l'esecuzione per l'uso.  È anche possibile usare l'> ActivityFunc<TResult e le alternative correlate quando il delegato che l'attività deve richiamare restituirà un valore.  

Infine, le attività possono essere convalidate per assicurarsi che siano configurate correttamente controllando le singole impostazioni, vincolando le attività figlio consentite e così via. Per la necessità comune di richiedere un argomento specifico, è possibile aggiungere un attributo RequiredArgument alla dichiarazione di proprietà nell'attività.  Per una convalida più complessa, nel costruttore dell'attività creare un vincolo e aggiungerlo alla raccolta di vincoli rilevati nella classe Activity.  Un vincolo è un'attività scritta per esaminare l'attività di destinazione e garantire la validità.  La figura 38 mostra il costruttore per l'attività Iterator, che convalida che la proprietà RequestedIterations è impostata. Infine, se si esegue l'override del metodo CacheMetadata, è possibile richiamare il metodo AddValidationError nel parametro ActivityMetdata per aggiungere errori di convalida per l'attività. 

var act = new DelegateInArgument<Iterator> { Name = "constraintArg" };

var vctx = new DelegateInArgument<ValidationContext>();

Vincolo<Iteratore> cons = new Constraint<Iterator>

{

    Body = new ActivityAction<Iterator, ValidationContext>

    {

        Argument1 = act,

        Argument2 = vctx,

        Gestore = new AssertValidation

        {

            Messaggio = "Iterazione deve essere specificata",

            PropertyName = "RequestedIterations",

            Asserzione = new InArgument<bool>(

                (e) => atto. Get(e). RequestedIterations != null)

        }

    }

};

base. Constraints.Add(cons);

figura 38: Vincoli

ActivityDesigner

Uno dei vantaggi di WF è che consente di programmare la logica dell'applicazione in modo dichiarativo, ma la maggior parte delle persone non vuole scrivere XAML manualmente, motivo per cui l'esperienza di progettazione in WF è così importante.  Durante la creazione di attività personalizzate, è anche probabile che si voglia creare una finestra di progettazione per fornire l'esperienza di visualizzazione e interazione visiva per i consumer dell'attività.  La finestra di progettazione per WF è basata su Windows Presentation Foundation, che significa avere tutta la potenza di applicazione di stili, trigger, databinding e tutti gli altri ottimi strumenti per la creazione di un'interfaccia utente avanzata per la finestra di progettazione.  Inoltre, WF fornisce alcuni controlli utente che è possibile usare nella finestra di progettazione per semplificare l'attività di visualizzazione di una singola attività figlio o di una raccolta di attività.  I quattro controlli principali sono:

  • ActivityDesigner: controllo WPF radice usato negli ActivityDesigner
  • WorkflowItemPresenter: usato per visualizzare una singola attività
  • WorkflowItemsPresenter: usato per visualizzare una raccolta di attività figlio
  • ExpressionTextBox: usato per abilitare la modifica sul posto di espressioni, ad esempio argomenti

WF4 contiene un modello di progetto ActivityDesignerLibrary e un modello di elemento ActivityDesigner che può essere usato per creare inizialmente gli artefatti.  Dopo aver inizializzato la finestra di progettazione, è possibile iniziare a usare gli elementi precedenti per personalizzare l'aspetto dell'attività specificando come visualizzare i dati e le rappresentazioni visive delle attività figlio.  All'interno della finestra di progettazione è possibile accedere a un oggetto ModelItem che è un'astrazione sull'attività effettiva e a visualizzare tutte le proprietà dell'attività. 

Il controllo WorkflowItemPresenter può essere usato per visualizzare una singola attività che fa parte dell'attività.  Ad esempio, per consentire di trascinare un'attività nell'attività Iterator illustrata in precedenza e visualizzare l'attività contenuta nell'attività Iteractor, puoi usare il codice XAML illustrato nella figura 39, associando WorkflowItemPresenter a ModelItem.Body.  La figura 40 mostra la finestra di progettazione in uso in un'area di progettazione del flusso di lavoro. 

<sap:ActivityDesigner x:Class="CustomActivities.Presentation.IteratorDesigner"

  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:sap="clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation"

  xmlns:sapv="clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation">

  > griglia di <

    <BorderBrush="MidnightBlue" BorderThickness="3" CornerRadius="10">

<sap:WorkflowItemPresenter MinHeight="50"

Item="{Binding Path=ModelItem.Body, Mode=TwoWay}"

HintText="Add body here" />

    </Border>

  </Grid>

</sap:ActivityDesigner>

figura 39: WorkflowItemPresenter nell'ActivityDesigner

figura 40: progettazione iteratore

WorkflowItemsPresenter offre funzionalità simili per la visualizzazione di più elementi, ad esempio gli elementi figlio in una sequenza. È possibile definire un modello e un orientamento per la modalità di visualizzazione degli elementi, nonché un modello di spacer per aggiungere elementi visivi tra attività quali connettori, frecce o qualcosa di più interessante.  La figura 41 mostra una semplice finestra di progettazione per un'attività di sequenza personalizzata che visualizza le attività figlio con una linea orizzontale tra ognuna.

<sap:ActivityDesigner x:Class="CustomActivities.Presentation.CustomSequenceDesigner"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:sap="clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation"

    xmlns:sapv="clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation">

    > griglia di <

      <StackPanel>

         <BorderBrush="Goldenrod" BorderThickness="3">

     <sap:WorkflowItemsPresenter HintText="Drop Activities Here"

  Items="{Binding Path=ModelItem.ChildActivities}">

     <sap:WorkflowItemsPresenter.SpacerTemplate>

  <datatemplate>

    <Rettangolo Height="3" Width="40"

 Fill="MidnightBlue" Margin="5" />

                     </DataTemplate>

                  </sap:WorkflowItemsPresenter.SpacerTemplate>

                  <sap:WorkflowItemsPresenter.ItemsPanel>

                      <ItemsPanelTemplate>

                          <StackPanel Orientation="Vertical"/>

                      </ItemsPanelTemplate>

                   </sap:WorkflowItemsPresenter.ItemsPanel>

            </sap:WorkflowItemsPresenter>

        </Border>

      </StackPanel>

    </Grid>

</sap:ActivityDesigner>

figura 41: Progettazione sequenza personalizzata con WorkflowItemsPresenter

Figura 42: Progettazione sequenza

Infine, il controllo ExpressionTextBox consente la modifica sul posto degli argomenti dell'attività all'interno della finestra di progettazione oltre alla griglia delle proprietà. In Visual Studio 2010 questo include il supporto intelliSense per le espressioni immesse. La figura 43 mostra una finestra di progettazione per l'attività GetManager creata in precedenza, abilitando la modifica degli argomenti EmployeeName e ManagerEmail all'interno della finestra di progettazione. La finestra di progettazione effettiva è illustrata nella figura 44. 

<sap:ActivityDesigner x:Class="CustomActivities.Presentation.GetManagerDesigner"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:sap="clr-namespace:System.Activities.Presentation;

assembly=System.Activities.Presentation"

    xmlns:sapv="clr-namespace:System.Activities.Presentation.View;

assembly=System.Activities.Presentation"

xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;

assembly=System.Activities.Presentation">

    <sap:ActivityDesigner.Resources>

        <sapc:ArgumentToExpressionConverter

x:Key="ArgumentToExpressionConverter"

x:Uid="swdv:ArgumentToExpressionConverter_1" />

    </sap:ActivityDesigner.Resources>

    > griglia di <

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="50" />

             <ColumnDefinition Width="*" />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition/>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <TextBlock VerticalAlignment="Center"

 HorizontalAlignment="Center">Employee:</TextBlock>

        <sapv:ExpressionTextBox Grid.Column="1"

              Expression="{Binding Path=ModelItem.EmployeeName, Mode=TwoWay,

       Converter={StaticResource ArgumentToExpressionConverter},

       ConverterParameter=In}" OwnerActivity="{Binding Path=ModelItem}"

MinLines="1" MaxLines="1" MinWidth="50"

HintText="< Nome dipendente>" />

        <TextBlock Grid.Row="1" VerticalAlignment="Center"

              HorizontalAlignment="Center">Mgr Email:</TextBlock>

        <sapv:ExpressionTextBox Grid.Column="2" Grid.Row="1"

    UseLocationExpression="True"

           Expression="{Binding Path=ModelItem.ManagerEmail, Mode=TwoWay,

    Converter={StaticResource ArgumentToExpressionConverter},

    ConverterParameter=Out}" OwnerActivity="{Binding Path=ModelItem}"

    MinLines="1" MaxLines="1" MinWidth="50" />

    </Grid>

</sap:ActivityDesigner>

figura 43: Uso di ExpressionTextBox per modificare gli argomenti nella finestra di progettazione

Figura 44: ActivityDesigner GetManager

I progettisti di esempio presentati qui sono stati semplici per evidenziare le funzionalità specifiche, ma la potenza completa di WPF è a vostra disposizione per rendere le attività più facili da configurare e più naturale da usare per i consumer.   Dopo aver creato una finestra di progettazione, esistono due modi per associarlo all'attività: l'applicazione di un attributo alla classe di attività o l'implementazione dell'interfaccia IRegisterMetadata.  Per consentire alla definizione dell'attività di guidare la scelta della finestra di progettazione, è sufficiente applicare DesignerAttribute alla classe di attività e fornire il tipo per la finestra di progettazione; questo funziona esattamente come ha fatto in WF3.  Per un'implementazione più ad accoppiamento debole, è possibile implementare l'interfaccia IRegisterMetadata e definire gli attributi da applicare alla classe di attività e alle proprietà.  La figura 45 mostra un'implementazione di esempio per applicare le finestre di progettazione per due attività personalizzate. Visual Studio rileverà l'implementazione e la richiamerà automaticamente, mentre un host della finestra di progettazione personalizzata, discusso successivamente, chiamerebbe il metodo in modo esplicito.

public class Metadata : IRegisterMetadata

{

    public void Register()

    {

        Generatore AttributeTableBuilder = new AttributeTableBuilder();

        muratore. AddCustomAttributes(typeof(SendMail), new Attribute[] {

            new DesignerAttribute(typeof(SendMailDesigner))});

        muratore. AddCustomAttributes(typeof(GetManager), new Attribute[]{

            new DesignerAttribute(typeof(GetManagerDesigner))});

        MetadataStore.AddAttributeTable(builder. CreateTable());

    }

}

figura 45: Registrazione delle finestre di progettazione per le attività

Rehosting della finestra di progettazione del flusso di lavoro

In passato, gli sviluppatori hanno spesso voluto fornire agli utenti la possibilità di creare o modificare flussi di lavoro.  Nelle versioni precedenti di Windows Workflow Foundation questo è stato possibile, ma non un'attività semplice.  Con il passaggio a WPF è disponibile una nuova finestra di progettazione e il rehosting è stato un caso d'uso principale per il team di sviluppo.  È possibile ospitare la finestra di progettazione in un'applicazione WPF personalizzata, come illustrato nella figura 46, con alcune righe di codice XAML e alcune righe di codice. 

figura 46: di progettazione del flusso di lavoro con rehosting

Il codice XAML per la finestra, figura 47, usa una griglia per il layout di tre colonne, quindi inserisce un controllo bordo in ogni cella.  Nella prima cella la casella degli strumenti viene creata in modo dichiarativo, ma può anche essere creata nel codice.  Per la visualizzazione della finestra di progettazione stessa e la griglia delle proprietà, sono presenti due controlli bordo vuoti in ognuna delle celle rimanenti.  Questi controlli vengono aggiunti nel codice. 

<Window x:Class="WorkflowEditor.MainWindow"

        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:sys="clr-namespace:System; assembly=mscorlib"

 xmlns:sapt="clr-namespace:System.Activities.Presentation.Toolbox;

assembly=System.Activities.Presentation"

        Title="Workflow Editor" Height="500" Width="700" >

    < > Window.Resources

        <sys:String x:Key="AssemblyName">System.Activities, Version=4.0.0.0,

Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>

        <sys:String x:Key="MyAssemblyName">CustomActivities</sys:String>

    </Window.Resources>

    <Grid x:Name="DesignerGrid">

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="2*" />

            <ColumnDefinition Width="7*" />

            <ColumnDefinition Width="3*" />

        </Grid.ColumnDefinitions>

        <border>

            <sapt:ToolboxControl>

                <sapt:ToolboxControl.Categories>

                    <sapt:ToolboxCategory CategoryName="Basic">

                        <sapt:ToolboxItemWrapper

  AssemblyName="{StaticResource AssemblyName}" >

                            <sapt:ToolboxItemWrapper.ToolName>

                                System.Activities.Statements.Sequence

                            </sapt:ToolboxItemWrapper.ToolName>

                        </sapt:ToolboxItemWrapper>

                        <sapt:ToolboxItemWrapper

AssemblyName="{StaticResource MyAssemblyName}">

                            <sapt:ToolboxItemWrapper.ToolName>

                                CustomActivities.SendMail

                             </sapt:ToolboxItemWrapper.ToolName>

                        </sapt:ToolboxItemWrapper>

                    </sapt:ToolboxCategory>

                </sapt:ToolboxControl.Categories>

            </sapt:ToolboxControl>

        </Border>

        <Border Name="DesignerBorder" Grid.Column="1"

BorderBrush="Salmon" BorderThickness="3" />

        <Border x:Name="PropertyGridExpander" Grid.Column="2" />

    </Grid>

</Window>

figura 47: XAML di rehosting della finestra di progettazione

Il codice per inizializzare la finestra di progettazione e la griglia delle proprietà è illustrato nella figura 48.  Il primo passaggio, nel metodo OnInitialized, consiste nel registrare le finestre di progettazione per tutte le attività che verranno usate nella finestra di progettazione del flusso di lavoro.  La classe DesignerMetadata registra le finestre di progettazione associate per le attività fornite con framework e la classe Metadata registra le finestre di progettazione per le attività personalizzate.  Creare quindi la classe WorkflowDesigner e usare le proprietà View e PropertyInspectorView per accedere agli oggetti UIElement necessari per il rendering.  La finestra di progettazione viene inizializzata con una sequenza vuota, ma è anche possibile aggiungere menu e caricare il codice XAML del flusso di lavoro da un'ampia gamma di origini, tra cui il file system o un database. 

public MainWindow()

{

    InitializeComponent();

}

protected override void OnInitialized(EventArgs e) {

    base. OnInitialized(e);

    RegisterDesigners();

    PlaceDesignerAndPropGrid();

}

private void RegisterDesigners() {

    new DesignerMetadata(). Register();

    new CustomActivities.Presentation.Metadata(). Register();

}

private void PlaceDesignerAndPropGrid() {

    Progettazione WorkflowDesigner = nuovo WorkflowDesigner();

    designer. Load(new System.Activities.Statements.Sequence());

    DesignerBorder.Child = designer. Vista;

    PropertyGridExpander.Child = designer. PropertyInspectorView;

}

figura 48: del codice di rehosting della finestra di progettazione

Con questo piccolo frammento di codice e markup, è possibile modificare un flusso di lavoro, trascinare e rilasciare attività dalla casella degli strumenti, configurare le variabili nel flusso di lavoro e modificare gli argomenti sulle attività.  Puoi anche ottenere il testo del codice XAML chiamando Flush nella finestra di progettazione, quindi facendo riferimento alla proprietà Text di WorkflowDesigner.  C'è molto di più che fare, e iniziare è molto più facile di prima. 

Servizi flusso di lavoro

Come accennato in precedenza, una delle grandi aree di interesse di questa versione di Windows Workflow Foundation è un'integrazione più stretta con Windows Communication Foundation.  Nell'esempio della procedura dettagliata dell'attività è stato illustrato come chiamare un servizio da un flusso di lavoro e in questa sezione verrà illustrato come esporre un flusso di lavoro come servizio WCF. 

Per iniziare, creare un nuovo progetto e selezionare il progetto servizio flusso di lavoro WCF. Una volta completato il modello, troverai un progetto con un file web.config e un file con un'estensione XAMLX.  Il file XAMLX è un servizio dichiarativo o un servizio flusso di lavoro e, come altri modelli WCF, fornisce un'implementazione del servizio funzionante, anche se semplice, che riceve una richiesta e restituisce una risposta.  Per questo esempio, cambierò il contratto per creare un'operazione per ricevere una nota spese e restituire un indicatore che indica che il report è stato ricevuto.  Per iniziare, aggiungere una classe ExpenseReport al progetto come quella illustrata nella figura 49.

[DataContract]

public class ExpenseReport

{

    [DataMember]

    public DateTime FirstDateOfName { get; set; }

    [DataMember]

    public double TotalAmount { get; set; }

    [DataMember]

    public string EmployeeName { get; set; }

}

Figura 49: Tipo di contratto rapporto spese

Tornare alla finestra di progettazione del flusso di lavoro, modificare il tipo della variabile "data" nelle variabili impostando il tipo ExpenseReport appena definito (potrebbe essere necessario compilare il progetto per il tipo da visualizzare nella finestra di dialogo selezione tipi). Aggiornare l'attività Di ricezione facendo clic sul pulsante Contenuto e modificando il tipo nella finestra di dialogo sul tipo ExpenseReport in modo che corrisponda.  Aggiornare le proprietà dell'attività per definire un nuovo OperationName e ServiceContractName, come illustrato nella figura 50. 

Figura 50: Configurazione del percorso di ricezione

Ora l'attività SendReply può avere valore impostato su True per restituire l'indicatore di ricevuta.  Ovviamente, in un esempio più complesso, è possibile usare la potenza completa del flusso di lavoro per salvare le informazioni nel database, eseguire la convalida del report e così via prima di determinare la risposta. Passando al servizio con l'applicazione WCFTestClient viene illustrato come la configurazione dell'attività definisce il contratto di servizio esposto, come illustrato nella figura 51. 

Figura 51: Contratto generato dal servizio flusso di lavoro

Dopo aver creato un servizio flusso di lavoro, è necessario ospitarlo, esattamente come per qualsiasi servizio WCF.  Nell'esempio precedente è stata usata l'opzione più semplice per l'hosting: IIS.  Per ospitare un servizio flusso di lavoro nel proprio processo, è necessario usare la classe WorkflowServiceHost disponibile nell'assembly System.ServiceModel.Activities.  Si noti che nell'assembly System.WorkflowServices è presente un'altra classe con lo stesso nome, usata per ospitare i servizi del flusso di lavoro compilati usando le attività WF3.  Nel caso più semplice dell'self-hosting, è possibile usare codice simile a quello illustrato nella figura 52 per ospitare il servizio e aggiungere endpoint. 

Host WorkflowServiceHost = nuovo

    WorkflowServiceHost(XamlServices.Load("ExpenseReportService.xamlx"),

    new Uri("https://localhost:9897/Services/Expense"));

ospite. AddDefaultEndpoints();

ospite. Description.Behaviors.Add(

    new ServiceMetadataBehavior { HttpGetEnabled = true });

ospite. Open();

Console.WriteLine("Host è aperto");

Console.ReadLine();

figura 52: Hosting automatico del servizio flusso di lavoro

Se si desidera sfruttare le opzioni di hosting gestite in Windows, è possibile ospitare il servizio in IIS copiando il file XAMLX nella directory e specificando le informazioni di configurazione necessarie per comportamenti, associazioni, endpoint e così via nel file web.config.  Infatti, come illustrato in precedenza, quando si crea un nuovo progetto in Visual Studio usando uno dei modelli di progetto Del servizio flusso di lavoro dichiarativo, si otterrà un progetto con un file web.config e il servizio flusso di lavoro definito in XAMLX, pronto per la distribuzione. 

Quando si usa la classe WorkflowServiceHost per ospitare il servizio flusso di lavoro, è comunque necessario essere in grado di configurare e controllare le istanze del flusso di lavoro.  Per controllare il servizio è presente un nuovo endpoint standard denominato WorkflowControlEndpoint.  Una classe complementare WorkflowControlClient fornisce un proxy client predefinito.  È possibile esporre un oggetto WorkFlowControlEndpoint nel servizio e usare WorkflowControlClient per creare ed eseguire nuove istanze di flussi di lavoro o controllare l'esecuzione di flussi di lavoro.  Si usa questo stesso modello per gestire i servizi nel computer locale oppure è possibile usarlo per gestire i servizi in un computer remoto.  La figura 53 mostra un esempio aggiornato di esposizione dell'endpoint di controllo del flusso di lavoro nel servizio. 

Host WorkflowServiceHost = nuovo

    WorkflowServiceHost("ExpenseReportService.xamlx",

    new Uri("https://localhost:9897/Services/Expense"));

ospite. AddDefaultEndpoints();

WorkflowControlEndpoint wce = new WorkflowControlEndpoint(

    new NetNamedPipeBinding(),

    new EndpointAddress("net.pipe://localhost/Expense/WCE"));

ospite. AddServiceEndpoint(wce);

ospite. Open();

figura 53: Endpoint di controllo del flusso di lavoro

Poiché non si crea direttamente WorkflowInstance, sono disponibili due opzioni per l'aggiunta di estensioni al runtime.  Il primo consiste nel fatto che è possibile aggiungere alcune estensioni a WorkflowServiceHost e inizializzate correttamente con le istanze del flusso di lavoro.  Il secondo consiste nell'usare la configurazione.  Il sistema di rilevamento, ad esempio, può essere interamente definito nel file di configurazione dell'applicazione.   Si definiscono i partecipanti di rilevamento e i profili di rilevamento nel file di configurazione e si usa un comportamento, si applica un partecipante specifico a un servizio, come illustrato nella figura 54. 

<system.serviceModel>

    < > di rilevamento

      <partecipanti>

        <add name="EtwTrackingParticipant"

             type="System.Activities.Tracking.EtwTrackingParticipant, System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

             profileName="HealthMonitoring_Tracking_Profile"/>

      </participants>

      > profili di <

        <trackingProfile name="HealthMonitoring_Tracking_Profile">

          <'attività del flusso di lavoroDefinitionId="*">

            <workflowInstanceQuery>

              < >

                <state name="Started"/>

                <state name="Completed"/>

              </states>

            </workflowInstanceQuery>

          </workflow>

        </trackingProfile>

      </profiles>

</tracking>

. . .

<comportamenti>

      <serviceBehaviors>

        <nome comportamento="SampleTrackingSample.SampleWFBehavior">

          <etwTracking profileName=" HealthMonitoring_Tracking_Profile" />

        </behavior>

      </serviceBehaviors>

</behaviors>

. . .

figura 54: Configurazione del rilevamento per i servizi

Esistono molti nuovi miglioramenti all'hosting e alla configurazione per i servizi WCF che è possibile sfruttare nei flussi di lavoro, ad esempio i servizi "senza svc" o i servizi che non necessitano di un'estensione di file, di associazioni predefinite e di endpoint predefiniti solo per citarne alcuni.  Per altre informazioni su queste e altre funzionalità in WCF4, vedere il documento complementare su WCF disponibile nella sezione Risorse aggiuntive. 

Oltre ai miglioramenti dell'hosting, Windows Workflow Foundation sfrutta altre funzionalità in WCF; alcuni direttamente e altri indirettamente.  Ad esempio, la correlazione dei messaggi è ora una funzionalità chiave nella creazione di servizi che consente di correlare i messaggi a una determinata istanza del flusso di lavoro in base ai dati nel messaggio, ad esempio un numero di ordine o un ID dipendente. La correlazione può essere configurata nelle varie attività di messaggistica sia per la richiesta che per la risposta e supporta la correlazione su più valori nel messaggio.  Anche in questo caso, altri dettagli su queste nuove funzionalità sono disponibili nel documento complementare su WCF4.   

Conclusione

La nuova tecnologia Windows Workflow Foundation fornita con .NET Framework 4 rappresenta un miglioramento significativo delle prestazioni e della produttività degli sviluppatori e realizza un obiettivo di sviluppo di applicazioni dichiarative per la logica di business.  Il framework fornisce gli strumenti per brevi unità di lavoro complesse da semplificare e per la creazione di servizi complessi a esecuzione prolungata con messaggi correlati, stato persistente e visibilità avanzata dell'applicazione tramite il rilevamento. 

Informazioni sull'autore

Matt è membro del personale tecnico di Pluralsight, dove si concentra sulle tecnologie dei sistemi connesse (WCF, WF, BizTalk, AppFabric e la piattaforma dei servizi di Azure). Matt è anche un consulente indipendente specializzato nella progettazione e nello sviluppo di applicazioni Microsoft .NET. In qualità di scrittore Matt ha contribuito a diverse riviste e riviste, tra cui MSDN Magazine, dove attualmente scrive il contenuto del flusso di lavoro per la colonna Foundations. Matt condivide regolarmente il suo amore per la tecnologia parlando in conferenze locali, regionali e internazionali come Tech Ed. Microsoft ha riconosciuto Matt come MVP per i suoi contributi della community per la tecnologia dei sistemi connessi. Contatta Matt tramite il suo blog: http://www.pluralsight.com/community/blogs/matt/default.aspx.

Risorse aggiuntive