L’objectif de ce tutoriel associé à la gestion des services de workflow, est d’utiliser le service de persistance pour voir la possibilité d’exécuter des processus de longue exécution
Nous prendrons comme étude de cas une opération d’embauche où le candidat fait obligatoirement un examen technique et un examen oral. Pour que sa candidature soit acceptée, il doit avoir une moyenne supérieure ou égale à 3 entre les deux examens.
Etape 1 – Création de la solution
L’objectif de créer la solution et les projets qui vont être utilisés.
- Lancez VS 2013
- Créez une application WPF appelée « Tutoriel41UI »
- Fermez les fenêtres « MainWindow » que nous modifierons ultérieurement
- A la solution ajoutez un nouveau projet de type « Bibliothèque d’Activite »
- Donnez le nom « EmbaucheLibrary » à ce projet
- Supprimez le workflow « Activity1 » à partir de l’explorateur de solutions
- Dans le projet « Tutoriel41UI » ajoutez des références pour « System.Activities » et « EmbaucheLibrary »
- Dans le projet « EmbaucheLibrary » ajoutez le fichier « AttendreReponse » créé dans les tutoriaux précédents
- Dans le projet « Tutoriel41UI » ajoutez le fichier « TextBoxTextWriter » créé dans le tutoriel précédent.
- Compilez la solution
Etape 2 : Création du workflow
L’objectif de cette étape est de créez un workflow de simulation d’opération d’embauche
- Créez un workflow de type organigramme appelé « EmbaucheWorkflow »
- Créez deux variables : « noteTechnique » et « noteOral » de type « Int32 »
- Créez un argument de type « Int32 » en sortie appelé « moyenne » qui servira à calculer la moyenne du candidat
- Ajoutez une activité « Pick » appelée « Lecture Note Technique » et connectez-la à l’activité de démarrage
- Double-cliquez pour modifier l’activité
- Supprimez la deuxième branche
- Glissez une activité « AttendreReponse » de type « Int32 »
- Dans les propriétés « Destinataire », « NomSignet » et « Result », affectez respectivement « Commission Technique », « EvaluationTechnique » et « noteTechnique »
- Ajoutez une activité « Pick » et appelez-la « Lecture Note Orale ». Connectez-la au premier « Pick »
- Supprimez la deuxième branche de cette activité
- Glissez une activité « AttendreReponse » de type « Int32 »
- Dans les propriétés « Destinataire », « NomSignet » et « Result », affectez respectivement « Commission Orale », « EvaluationOrale » et « noteOrale »
- Glissez une activité « Assign » et connectez-la au deuxième « Pick »
- Affectez à travers cette activité la moyenne des deux notes à l’argument « moyenne »
- Le workflow devrait être similaire à ceci :
Etape 3 : Création de la base de données de persistance
L’objectif de cette étape est de créer les bases de données SQL Server qui gèreront la persistance des workflows
- Lancez SQL Server Management Studio
- Créez une base de données appelée « FormationWF »
- Dans le répertoire « C:\Windows », naviguez jusqu’au sous-répertoire « SQL » du répertoire du .NET Framework
- Remarquez la présence de deux fichiers « SqlWorkflowInstanceStoreLogic.sql » et « SqlWorkflowInstanceStoreSchema.sql »
- Ouvrez ces deux scripts dans Management Studio et exécutez-le sur la base « FormationWF » en commençant par « SqlWorkflowInstanceStoreLogic.sql»
- Remarquez les nouvelles tables qui ont été créées
Etape 4 : Création de l’interface
L’objectif de cette étape est de créer la fenêtre principale WPF composée des éléments suivants :
- Un bouton créant un dossier d’une nouvelle candidature
- U bouton permettant de donner une note technique
- Un bouton permettant de donner une note orale
- Une liste déroulante contenant la liste des anciennes candidatures
- Une zone de texte interceptant l’affichage en console du workflow
Etapes :
- Ouvrez le fichier « MainWindows.xaml »
- Remplacez le XAML en cours par le XAML suivant :
<Window x:Class="Tutoriel41UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250px"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<Label Content="Nom :"/>
<TextBox Name="txtNom" Margin="5"/>
<Button Name="btnDemarrer" Margin="5" Content="Nouvelle Candidature" />
<ComboBox Margin="5" SelectedIndex="0" Name="cbEval" >
<ListBoxItem Content="1"/>
<ListBoxItem Content="2"/>
<ListBoxItem Content="3"/>
<ListBoxItem Content="4"/>
<ListBoxItem Content="5"/>
</ComboBox>
<Button Name="btnTechnique" Content="Evaluer Technique" Margin="5" />
<Button Name="btnOral" Content="Evaluer Oral" Margin="5" />
<Label Content="Workflow :" Margin="5"/>
<ComboBox Margin="5" Name="cbWorkflows" />
</StackPanel>
<TextBox Name="txtConsole" Grid.Column="1" TextWrapping="Wrap" AcceptsReturn="True" />
</Grid>
</Window>
- Ajoutez les méthodes d’activation / désactivation des boutons :
private void DesactiverBoutons()
{
ActiverBouton(btnOral, false);
ActiverBouton(btnTechnique, false);
}
private void ActiverBouton(Button bouton, bool valeur)
{
bouton.Dispatcher.BeginInvoke(new Action(() => bouton.IsEnabled = valeur));
}
- Nous allons mettre la liste des workflows dans une liste observble déclarée comme ceci :
/// <summary>
/// liste des ids des workflows
/// </summary>
ObservableCollection<Guid> _liste;
- Ajoutez un using « Tutoriel32UI » ou le nom de l’espace de nom contenant la classe « TextBoxTextWriter »
- Ajoutez un évènement « Load » à la grille (Grid) principale
- Dans l’évènement, « Grid_Load » redirigez la sortie de la console comme ceci :
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Console.SetOut(new TextBoxTextWriter(txtConsole));
DesactiverBoutons();
// configurer le store
ConfigurerStore();
// charger les workflows en cours
ChargerListeWorkflows();
}
- La méthode « Grid_Loaded » fait appel à deux méthodes qui n’existent pas. Ajoutez deux méthodes vides appelées « ConfigurerStore » et « ChargerListeWorkflows »
/// <summary>
/// charge la liste des workflows à partir de la base de données
/// </summary>
private void ChargerListeWorkflows()
{
_liste = new ObservableCollection<Guid>();
}
cbWorkflows.ItemsSource = _liste;
}
/// <summary>
/// configure le store SQL Server
/// </summary>
private void ConfigurerStore()
{
}
- La fenêtre devrait être comme suit :
- Compilez la solution pour vérifier la présence d’erreurs.
Etape 5 : Création d’un participant persistant
Le service de persistance permet de sauvegarder le workflow et son état sur une base de données. Un participant persistant permet d’ajouter des informations additionnelles qui vont être persistées avec le workflow.
L’objectif de cette étape est de créer la classe « CandidatParticipant » qui contiendra des informations sur le candidat qui vont être persistées avec le workflow.
- Dans le projet « EmbaucheLibrary » ajoutez une nouvelle classe appelée « CandidatParticipant »
- La classe « CandidatParticipant » doit hériter de la classe « PersistenceParticipant »
- Ajoutez des « using » vers les espaces de noms « System.Activities.Persistence » et « using System.Xml.Linq »
public class CandidatParticipant : PersistenceParticipant
{
}
- Les propriétés à persister doivent être conformes à des espaces de nom XML. Ainsi, la propriété « Nom » sera identifiée par un espace de nom unique.
- Déclarez deux variables publiques statiques « formationns » et « nomns » de type « XNamespace » et « XName » comme ceci :
/// <summary>
/// espace de nom de la formation
/// </summary>
static XNamespace formationns = XNamespace.Get("urn:formationwf");
/// <summary>
/// espace de nom de la propriété "nom"
/// </summary>
public static XName nomns = formationns.GetName("Nom");
- Ajoutez une propriété publique de type « String » appelée « Nom » qui contiendra le nom du candidat
/// <summary>
/// nom du candidat
/// </summary>
public string Nom { get; set; }
- La méthode “CollectValues” doit être redéfinie de façon à préparer les données qui vont être intégrées lors de la persistance du workflow :
/// <summary>
/// collecter le nom pour le sauvegarder sur la BDD
/// </summary>
/// <param name="readWriteValues"></param>
/// <param name="writeOnlyValues"></param>
protected override void CollectValues(out IDictionary<System.Xml.Linq.XName, object> readWriteValues, out IDictionary<System.Xml.Linq.XName, object> writeOnlyValues)
{
Console.WriteLine("==============");
Console.WriteLine("Sauvegarde dossier {0}", Nom);
Console.WriteLine("==============");
// créer un dictionnaire qui contient le nom indexé par une clé de type espace de nom
readWriteValues = new Dictionary<XName, object>(1) { { nomns, Nom } };
writeOnlyValues = null;
}
- La méthode « PublishValues » doit être redéfinie de façon à charger les valeurs enregistrées sur la BDD sur les propriétés du participant :
/// <summary>
/// publier les valeurs à partir du référentiel
/// </summary>
/// <param name="readWriteValues"></param>
protected override void PublishValues(IDictionary<XName, object> readWriteValues)
{
object data;
if (readWriteValues.TryGetValue(nomns, out data))
{
if (data != null)
{
Nom = data.ToString();
Console.WriteLine("==============");
Console.WriteLine("Chargement dossier {0}", Nom);
Console.WriteLine("==============");
}
}
}
- Le listing complet de la classe devrait être comme ceci :
public class CandidatParticipant : PersistenceParticipant
{
/// <summary>
/// espace de nom de la formation
/// </summary>
static XNamespace formationns = XNamespace.Get("urn:formationwf");
/// <summary>
/// espace de nom de la propriété "nom"
/// </summary>
public static XName nomns = formationns.GetName("Nom");
/// <summary>
/// collecter le nom pour le sauvegarder sur la BDD
/// </summary>
/// <param name="readWriteValues"></param>
/// <param name="writeOnlyValues"></param>
protected override void CollectValues(out IDictionary<System.Xml.Linq.XName, object> readWriteValues, out IDictionary<System.Xml.Linq.XName, object> writeOnlyValues)
{
Console.WriteLine("==============");
Console.WriteLine("Sauvegarde dossier {0}", Nom);
Console.WriteLine("==============");
// créer un dictionnaire qui contient le nom indexé par une clé de type espace de nom
readWriteValues = new Dictionary<XName, object>(1) { { nomns, Nom } };
writeOnlyValues = null;
}
/// <summary>
/// publier les valeurs à partir du référentiel
/// </summary>
/// <param name="readWriteValues"></param>
protected override void PublishValues(IDictionary<XName, object> readWriteValues)
{
object data;
if (readWriteValues.TryGetValue(nomns, out data))
{
if (data != null)
{
Nom = data.ToString();
Console.WriteLine("==============");
Console.WriteLine("Chargement dossier {0}", Nom);
Console.WriteLine("==============");
}
}
}
/// <summary>
/// nom du candidat
/// </summary>
public string Nom { get; set; }
}
- Compilez pour vérifier l’absence d’erreurs.
Veuillez visiter la partie 2 de ce tutoriel pour le compléter.