Cet article détaille rapidement les étapes que vous devez effectuer afin d'activer la prise en charge de la Windows Workflow Foundation (WF) dans ASP.NET (classique ou MVC). Utilisons une très petite application de démonstration pour vous montrer comment vous pouvez le faire.
L'intégration de WF dans ASP.NET est quelque chose que je devais faire dans les derniers projets sur lesquels j'ai travaillé. J'ai donc pensé que je construis un petit exemple d'application qui vous montre le strict minimum de code requis pour y parvenir. Créons un exemple de demande étape par étape ...
Remarque : Cet article n'est pas une introduction sur WF. Une certaine compréhension de base de WF est requise.
Démarrez SQL Server Management Studio et créez une nouvelle base de données appelée "Workflow". Vous devez créer quelques tables et logique (procédures stockées et autres) afin de soutenir la persistance pour WF. Dans le répertoire "C: Windows Microsoft.net Framework V3.0 Windows Workflow Foundation SQL en" Vous trouverez 4 fichiers SQL. Exécutez les deux suivantes dans cet ordre:
Cela crée deux tables et 10 procédures stockées nécessaires afin de prendre en charge la persistance pour WF dans une base de données SQL Server. Téléchargez maintenant le code source de cet article, extrayez-le et exécutez le script DDL.SQL. Ce script crée une table appelée client.
Vous devez vous retrouver avec une structure de base de données qui ressemble à ceci:
Figure 1 - Base de données de workflow

Lorsque vous ouvrez l'exemple de solution de cet article, vous trouverez un projet de bibliothèque de code appelé "base de données". Tout ce qu'il contient est un fichier LINQ TO SQL qui contient une entité, à savoir le client.
Figure 2 - Entité client

L'exemple d'application vous permet de créer des clients. Tout ce que vous avez à faire est de saisir un nom d'utilisateur. Ensuite, un nouveau flux de travail est démarré et vous pouvez choisir d'approuver le client, de le rejeter ou de ne rien faire. Si vous n'agissez pas, le client sera supprimé après un certain temps mort.
Figure 3 - Le flux de travail

Dans cet échantillon, il y a un flux de travail de la machine d'état. Pour un tel flux de travail, vous devez fournir un état initial et un état terminé. Comme vous pouvez le voir dans la figure ci-dessus, il ne contient que deux états qui sont nommés de manière appropriée.
Ce flux de travail nécessite un paramètre appelé nom d'utilisateur. Lorsqu'il arrive à l'état initial, la stateInitialization Iactivity est exécutée et un nouveau client est créé et persisté dans le tableau client. Par défaut, tous les clients nécessitent l'approbation.
Figure 4 - Les clients en attente de l'approbation

J'ai mis ce workflow dans un projet séparé.
Figure 5 - La solution

Lorsque la stateInitialization Iactivity a terminé l'exécution du workflow écoutera l'un des 3 événements possibles:
L'événement de temps mort repose sur une activité de retard. Vous n'avez pas à licencier vous-même cet événement, mais vous êtes en charge de licencier les deux autres événements. J'ai donc créé un service local.
Listing 1 - Interface iCustomerService
[ ExternalDataExchange ]
public interface ICustomerService
{
event EventHandler < ExternalDataEventArgs > Approved ;
event EventHandler < ExternalDataEventArgs > Rejected ;
}Vous ne pouvez enregistrer qu'une seule implémentation pour un service local dans l'exécution du workflow et l'implémentation du service de clients ressemble à ceci:
Listing 2 - Service de clientèle
public class CustomerService : ICustomerService
{
public event EventHandler < ExternalDataEventArgs > Approved ;
public event EventHandler < ExternalDataEventArgs > Rejected ;
private bool FireEvent ( EventHandler < ExternalDataEventArgs > theEvent , ExternalDataEventArgs args )
{ //...}
public bool OnApproved ( Guid instanceId )
{
return FireEvent ( Approved , new ExternalDataEventArgs ( instanceId ) ) ;
}
public bool OnRejected ( Guid instanceId )
{
return FireEvent ( Rejected , new ExternalDataEventArgs ( instanceId ) ) ;
}
}Ce service local vous permet de déclencher l'événement approuvé ou rejeté en spécifiant l'ID d'instance (GUID) du workflow que vous souhaitez continuer à exécuter.
Il n'y a pas beaucoup de logique impliquée dans la création, l'approbation ou le rejet des clients, mais j'ai mis tout cela dans la classe CustainManager. Les méthodes sont explicites. Cette classe vous permet de récupérer une liste de tous les clients, d'ajouter de nouveaux clients, d'approuver, de rejeter et de supprimer les clients.
Listing 3 - Classe CustomerManager
public class CustomerManager
{
public IEnumerable < Customer > GetCustomers ( ) { //... }
public Customer GetCustomerById ( Guid customerId ) { //... }
public void ApproveCustomer ( Guid customerId ) { //... }
public void RejectCustomer ( Guid customerId ) { //... }
public void DeleteCustomer ( Guid customerId ) { //... }
public void AddCustomer ( Guid customerId , string userName ) { //... }
}Le workflow utilise ce type CustainManager pour effectuer tous ses travaux.
Voila, les fondations sont posées. Vous avez configuré une base de données qui prend en charge la persistance de WF, créé un flux de travail pour créer, approuver et rejeter les clients et créer un service local qui vous aidera à diriger vos instances de workflow quoi faire.
Maintenant, créons une nouvelle application Web ASP.NET qui mettra tout cela à fonctionner. J'ai trouvé le nom d'origine "Webapplication" pour le projet ASP.NET.
Figure 6 - Projet Webapplication Web

Le site ne contient qu'une seule page appelée Default.aspx. Cette page affiche une liste de clients, vous permet d'ajouter de nouveaux clients et d'approuver ou de rejeter ceux existants.
Figure 7 - Page default.aspx

Configurons l'application Web afin qu'il prenne en charge WF. Le code de configuration illustré dans cet article est abrégé pour la lisibilité. Téléchargez l'exemple de code source pour la version complète.
Ouvrez le fichier de configuration de votre application (web.config) et ajoutez la ligne suivante à la section ConfigurationSections.
< configSections >
<!-- ... -->
< section name = " WorkflowRuntime "
type = " System.Workflow.Runtime.Configuration.WorkflowRuntimeSection...etc. " />
</ configSections >Vous devez ajouter cette ligne au nœud de configuration afin que .NET sait qu'il doit utiliser le type WorkflowRuntimeSection pour lire le bit de configuration suivant que vous devez ajouter au nœud de configuration.
< WorkflowRuntime >
< CommonParameters >
< add name = " ConnectionString " value = " your connection string " />
</ CommonParameters >
< Services >
< add type = " System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService,...etc. "
useActiveTimers = " true " />
< add type = " System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService,...etc. "
UnloadOnIdle = " true " LoadIntervalSeconds = " 5 " />
</ Services >
</ WorkflowRuntime >Le nœud WorkflowRuntime charge les services manualworkflowschedulerservice et sqlworkflowPersisterServest pour le workflow Runtime. Vous devez charger le SQLWorkFlowPersisSistenceService si vous souhaitez persister vos workflows et pour ASP.NET, il est conseillé d'utiliser le service ManualWorkFlowsCheDulerservice au lieu du service defauftworkflowsCheDulerservice.
Chaque instance de workflow s'exécute sur un thread séparé. Le Workflow Runtime gère cela à l'aide d'un service de planificateur de workflow. Par défaut, le runtime utilise le type defauftworkFlowsCheDulerservice. Toutes les instances de workflow s'exécutent de manière asynchrone.
Pour ASP.NET, il est préférable d'utiliser le manualworkflowschedulerservice, car il vous permet d'exécuter des flux de travail de manière synchrone. Le Workflow Runtime utilise le thread d'appel de l'application hôte dans ce cas.
Cela vous permet d'attendre une réponse du workflow avant que ASP.NET n'envoie une réponse. Bien sûr, il n'est pas sage d'exécuter des tâches de longue durée dans vos flux de travail dans ce cas. (PS: consultez cet article de blog pour plus d'informations sur ce sujet.)
Lorsque vous utilisez le manualworkflowschedulerservice, assurez-vous de définir la propriété UseActiveTimers sur true. Si vous laissez ce paramètre défini sur sa valeur par défaut de false, toute activité de retard ne reprendra pas automatiquement après leur expiration. S'il est défini sur true, l'exécution du flux de travail utilisera un minuteur en mémoire pour vérifier périodiquement les activités de retard expirées.
Maintenant que votre application a été configurée pour Utiliser Workflow Foundation, vous devez héberger l'exécution du flux de travail.
Dans ASP.NET, l'endroit idéal pour le faire est dans le fichier global.asax. Ajoutez simplement le code suivant au gestionnaire d'événements Application_Start (...).
Listing 4 - Global.asax Application_start
protected void Application_Start ( object sender , EventArgs e )
{
WorkflowRuntime runtime = new WorkflowRuntime ( "WorkflowRuntime" ) ;
ExternalDataExchangeService exchangeService = new ExternalDataExchangeService ( ) ;
runtime . AddService ( exchangeService ) ;
CustomerService customerService = new CustomerService ( ) ;
exchangeService . AddService ( customerService ) ;
runtime . StartRuntime ( ) ;
Application [ "WorkflowRuntime" ] = runtime ;
}Une nouvelle instance du workflowruntime est créée, puis l'ExternalDataExchangeService est créée et ajoutée à l'exécution. Ensuite, nous créons une instance de notre service local icustomerService et l'ajoutons à l'instance externaldataExchangeService. Enfin, l'exécution est démarrée et stockée dans l'état de l'application.
De même, vous devez arrêter l'exécution du flux de travail lorsque l'application se termine comme indiqué dans la liste 5.
Listing 5 - Global.asax Application_end
protected void Application_End ( object sender , EventArgs e )
{
WorkflowRuntime runtime = Application [ "WorkflowRuntime" ] as WorkflowRuntime ;
if ( runtime != null )
{
runtime . StopRuntime ( ) ;
runtime . Dispose ( ) ;
}
} Lorsque vous cliquez sur le bouton Ajouter, vous devez démarrer un nouveau workflow. Lorsque vous utilisez le manualworkflowschedulerservice, vous contrôlez totalement vos instances de workflow. Dans Listing 6, vous pouvez voir que vous devez suivre ces étapes:
Listing 6 - Créez un nouveau client
WorkflowRuntime runtime = ( WorkflowRuntime ) Application [ "WorkflowRuntime" ] ;
Dictionary < string , object > arguments = new Dictionary < string , object > ( ) ;
arguments . Add ( "UserName" , txtUserName . Text . Trim ( ) ) ;
WorkflowInstance workflow = runtime . CreateWorkflow ( typeof ( CustomerApprovalWorkflow ) , arguments ) ;
workflow . Start ( ) ;
ManualWorkflowSchedulerService scheduler =
( ManualWorkflowSchedulerService ) runtime . GetService ( typeof ( ManualWorkflowSchedulerService ) ) ;
scheduler . RunWorkflow ( workflow . InstanceId ) ; Après avoir créé un client, vous pouvez approuver ou rejeter son compte. En d'autres termes, vous devez continuer le flux de travail qui a été lancé lorsque vous avez créé son compte.
Le code affiché dans Listing 7 est similaire à celui de Listing 6. La seule différence est que vous devez récupérer l'instance de votre service local (CustomerService) et l'appeler la méthode sur (...) onapproved. La méthode onapprovée déclenche l'événement approuvé. Après une livraison réussie, vous pouvez demander au planificateur de continuer à gérer le workflow.
Listing 7 - Approuver un client
WorkflowRuntime runtime = ( WorkflowRuntime ) Application [ "WorkflowRuntime" ] ;
ManualWorkflowSchedulerService scheduler =
( ManualWorkflowSchedulerService ) runtime . GetService ( typeof ( ManualWorkflowSchedulerService ) ) ;
CustomerService customerService = ( CustomerService ) runtime . GetService < ICustomerService > ( ) ;
if ( customerService . OnApproved ( customerId ) )
{
scheduler . RunWorkflow ( customerId ) ;
}Rejeter le compte d'un client est similaire. Vous n'avez qu'à envoyer l'événement rejeté au lieu de l'événement approuvé. Si vous n'agissez pas, l'activité de retard sera déclenchée après une minute et le compte du client sera automatiquement supprimé.
Et c'est tout ce qu'il y a.