Tutoriel 5.1 - Création d’un service de recrutement–Partie 2

Dans la partie précédente, nous avons créé les services et les workflows nécessaires à notre opération de recrutement. Durant cette partie, nous ferons le reste : les applications clientes. Etape 7 : Création de l’application candidat L’objectif de cette étape est de créer l’application « Candidat » qui permettra à un postulant de soumettre une candidature. Cette application sera également serveur puisqu’elle hébergera un service de notification. Ouvrez Visual Studio dans une nouvelle fenêtre en mode administrateur Créez une nouvelle application WPF et appelez-la « CandidatApp » Ouvrez « MainWindow » en mode design Supprimez la grille Glissez un « DockPanel » sur la fenêtre principale avec les propriétés « HorizontalAlignment » et « VerticalAlignment » à « Stretch », supprimez les propriétés « Height » et « Width ». « LastChildFill » doit être «True » Glissez un « Label » sur le « StackPanel » avec la propriété « Content » à « Nom : » et « Margin » à 3 et « DockPanel.Dock » à « Top » Glissez un « TextBox » avec la propriété « Name » à « txtNom » et « Margin » à 3 et « DockPanel.Dock » à « Top » Glissez un deuxième « Label » en dessous de la « TextBox » avec la propriété « Content » à « Date de naissance : » et « Margin » à 3 et « DockPanel.Dock » à « Top » Glissez un « DatePicker » en dessous du deuxième « Label » avec les propriétés « Name » à « dpDate », « Margin » à 3 et « DockPanel.Dock » à « Top » Ajoutez un bouton en dessous du « Picker » avec « Name », « Margin », « Content » à « btnSoumettre », 3 et « Soumettre » Glissez un « TextBox » en dessous du bouton  avec la propriété « Name » à « txtConsole », « Margin » à 3, « AcceptReturn » à « True » Le code XAML de la fenêtre principale doit être comme ceci : <Window x:Class="CandidatApp.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"> <DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" LastChildFill="True" > <Label Content="Nom :" Margin="3" DockPanel.Dock="Top"/> <TextBox Name="txtNom" Margin="3" DockPanel.Dock="Top"/> <Label Content="Date de Naissance :" Margin="3" DockPanel.Dock="Top"/> <DatePicker Name="dpDate" Margin="3" DockPanel.Dock="Top"/> <Button Content="Soumettre" DockPanel.Dock="Top" Margin="3" Name="btnSoumettre"/> <TextBox Name="txtConsole" Margin="3" AcceptsReturn="True" TextWrapping="Wrap" VerticalAlignment="Stretch"/> </DockPanel></Window> Lancez la boîte de dialogue d’ajout de nouvel élément (au projet CandidatApp) à partir de l’explorateur de solutions Dans la zone de recherche, tapez « WCF » Sélectionnez « Service WCF » Dans la zone « Nom », entrez « NotificationService » Remarquez que VS crée une interface appelée « INotificationService » et son implémentation « NotificationService Ouvrez le fichier « INotificationService.cs » Supprimez la déclaration de méthode « DoWork » Ajoutez une méthode de type « void » appelée « Notifier » et qui a deux paramètres « Nom » et « Acceptation » de type « String » et « Boolean » Décorez la méthode par l’attribut « OperationContract » pour la rendre comme service Le listing de l’interface INotificationService devrait être comme ceci : [ServiceContract] public interface INotificationService { [OperationContract] void Notifier(string Nom, bool Acceptation); } Ouvrez le fichiez « NotificationService.cs » Supprimez le code de « DoWork » Implémentez la méthode « Notifier » comme ceci : public void Notifier(string Nom, bool Acceptation) { string etat; if (Acceptation) etat = "accepté"; else etat = "rejeté"; Console.WriteLine("Le candidat {0} a été {1} ", Nom, etat);  } Compilez l’application. Ajoutez au projet la classe « TextBoxTextWriter » créé lors des tutoriaux précédents pour rediriger les sorties de la console sur la « TextBox » txtConsole Ouvrez « MainWindow.xaml.cs » Ajoutez un using sur l’esapce de nom de la classe « TextBoxTextWriter » Ajoutez un using sur « System.ServiceModel » Ajoutez un évènement de chargement « Loaded » sur le « DockPanel » Redirigez la sortie de la console// rediriger la sortie de la console Console.SetOut(new TextBoxTextWriter(txtConsole)); Créez ensuite le hôte qui permettra d’herbeger le service WCF comme ceci : // créer le hote wcf var host = new ServiceHost(typeof(NotificationService)); // lancer le hote wcf host.Open(); Console.WriteLine("service de notification démarré"); Nous allons enfin affectez une valeur par défaut à la date de naissance qui est de 20 ans avant aujourd’hui// date par défaut dpDate.SelectedDate = DateTime.Now.AddYears(-20); Le listing complet de la méthode devrait être comme ceci : private void DockPanel_Loaded(object sender, RoutedEventArgs e) { // rediriger la sortie de la console Console.SetOut(new TextBoxTextWriter(txtConsole)); // créer le hote wcf var host = new ServiceHost(typeof(NotificationService)); // lancer le hote wcf host.Open(); Console.WriteLine("service de notification démarré"); // date par défaut dpDate.SelectedDate = DateTime.Now.AddYears(-20); } Ouvrez le fichier « App.Config » Changez la propriété « baseAddress » de l’hôte à http://localhost:7766/Notification Le listing de « App.Config » devrait être comme ceci : <?xml version="1.0" encoding="utf-8" ?><configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="CandidatApp.NotificationService"> <endpoint address="" binding="basicHttpBinding" contract="CandidatApp.INotificationService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:7766/Notification" /> </baseAddresses> </host> </service> </services> </system.serviceModel></configuration> Lancez l’application pour tester le service Appelez la méthode « Notifier » à partir du testeur « WCF » Remarquez que les messages de notifications sortent sur la console Etape 8 : Création du proxy vers le workflow Dans l’étape précédente, nous avons créé le serveur de notification qui permettra au workflow de notifier le candidat. Dans cette étape, nous allons nous connecter au service du workflow pour pouvoir soumettre le dossier du candidat. Revenez sur la fenêtre VS qui contient le projet « Tutoriel51 » Lancez l’application « WorkflowConsoleHost » Revenez sur le projet « CandidatApp » Dans l’explorateur de solutions, cliquez sur le bouton droit sur les références puis sur « ajouter une référence de service » Dans la zone « Adresse », entrez http://localhost:9988/EmbaucheSimple Cliquez sur « Go » Dans l’espace de noms, entrez « FormationWF » Dans les services, sélectionnez « EmbaucheSimpleService » puis « IEmbaucheService » Cliquez sur « OK » Ouvrez « MainWndow » en mode design Affectez un évènement « Click » au bouton Créons les informations à transmettre à partir du formulaire // créer les informations var info = new FormationWF.CandidatInfo() { Nom = txtNom.Text, DateNaissance = dpDate.SelectedDate.Value }; Créons ensuite le proxy qui nous permettra de nous connecter au serveur// créer le proxy var client = new FormationWF.EmbaucheServiceClient(); Nous allons ensuite créer les données à transmettre au serveur // données à transmettre var data = new FormationWF.RecevoirCandidature(); data.pInfo = new FormationWF.CandidatInfo() { Nom = txtNom.Text, DateNaissance = dpDate.SelectedDate.Value }; Ensuite nous allons appeler le service et récupérer le résultat // appel du service var res = client.RecevoirCandidature(data); if (res.HasValue && !res.Value) Console.WriteLine("Candidature automatiquement rejetée"); Le listing complet du gestionnaire de clic est comme suit : private void btnSoumettre_Click(object sender, RoutedEventArgs e) { // créer les informations var info = new FormationWF.CandidatInfo() { Nom = txtNom.Text, DateNaissance = dpDate.SelectedDate.Value }; // créer le proxy var client = new FormationWF.EmbaucheServiceClient(); // données à transmettre var data = new FormationWF.RecevoirCandidature(); data.pInfo = new FormationWF.CandidatInfo() { Nom = txtNom.Text, DateNaissance = dpDate.SelectedDate.Value }; // appel du service var res = client.RecevoirCandidature(data); // afficher résultat si rejet auto if (res.HasValue && !res.Value) Console.WriteLine("Candidature automatiquement rejetée"); } Exécutez l’application puis remarquez comment des candidatures de moins de 30 ans sont automatiquement rejetées Etape 9 : Création de l’application de l’évaluateur L’objectif de cette étape est de créer une application qui va être utilisée par l’évaluateur pour évaluer les candidatures. Ouvrez Visual Studio dans une nouvelle fenêtre Créez une nouvelle application WPF et appelez-la « EvaluateurApp » Ouvrez « MainWindow » en mode design Supprimez la grille Glissez un « StackPanel » sur la fenêtre principale avec les propriétés « HorizontalAlignment » et « VerticalAlignment » à « Stretch », supprimez les propriétés « Height » et « Width ». Glissez un « Label » sur le « StackPanel » avec la propriété « Content » à « Nom : » et « Margin » à 3 Glissez un « TextBox » avec la propriété « Name » à « txtNom » et « Margin » à 3 Glissez un deuxième « Label » en dessous de la « TextBox » avec la propriété « Content » à « Note : » et « Margin » à 3 Glissez une « ComboBox » en dessous du deuxième « Label » avec les propriétés « Name » à « cbNote », « Margin » à 3. La combobox contient 5 notes de 1 à 5 et la propriété « SelectedIndex » à 0 Ajoutez un bouton en dessous du « ComboBox » avec « Name », « Margin », « Content » à « btnEvaluer », 3 et « Evaluer » Le XAML de la fenêtre devrait être comme ceci : <Window x:Class="EvaluateurApp.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> <StackPanel> <Label Content="Nom :" Margin="3"/> <TextBox TextWrapping="Wrap" Name="txtNom" Margin="3"/> <Label Content="Note :" Margin="3"/> <ComboBox Name="cbNote" Margin="3" SelectedIndex="0"> <ListBoxItem Content="1"/> <ListBoxItem Content="2"/> <ListBoxItem Content="3"/> <ListBoxItem Content="4"/> <ListBoxItem Content="5"/> </ComboBox> <Button Content="Evaluer" Name="btnEvaluer"/> </StackPanel>  </Grid></Window> Dans l’explorateur de solutions, cliquez sur le bouton droit sur les références puis sur « ajouter une référence de service » Dans la zone « Adresse », entrez http://localhost:9988/EmbaucheSimple Cliquez sur « Go » Dans l’espace de noms, entrez « FormationWF » Dans les services, sélectionnez « EmbaucheSimpleService » puis « IEmbaucheService » Cliquez sur « OK » Ajoutez un gestionnaire de clic au bouton « Evaluer » qui permet d’appeler la méthode « Evaluer » du service private void btnEvaluer_Click(object sender, RoutedEventArgs e) { // créer le proxy var proxy = new FormationWF.EmbaucheServiceClient(); // appeler le service proxy.Evaluer(txtNom.Text, cbNote.SelectedIndex + 1); } Compilez l’application pour vérifier d’éventuelles erreurs. Etape 10 : Finalisation du workflow L’objectif de cette étape est de finaliser le workflow de façon à ce qu’après l’évaluation, il envoie une notification au candidat. Revenez à la solution « Tutoriel51 » Si l’application console est en exécution, quittez-la Glissez une activité « Send » au workflow Connectez le dernier « Assign » avec l’activité « Send » Connectez le nouveau « Send » avec le côté « False » de l’activité de décision Dans la zone « OperationName », entrez « Notifier » Cliquez sur le bouton « Contenu » Ajoutez un paramètre appelé « Nom » de type « String » et dont la valeur est « info .Nom » Ajoutez un deuxième paramètre appelée « Acceptation », de type « Boolean » et dont la valeur est « Resultat »   Cliquez sur « OK » Dans la propriété « EndPoint.AddressUri » de l’activité « Send », entrez http://localhost:7766/Notification. C’est l’adresse du service de notification. Dans la propriété « EndPoint.Binding », de l’activité « Send », sélectionnez « basicHttpBinding » Dans la propriété « ServiceContractName » entrez « INotificationService » Le workflow devrait être comme ceci : Etape 11 : Mise en place de la corrélation Le workflow dans l’étape précédente est fonctionnel mais il reste un problème : lorsque l'âge du candidat dépasse 30 ans, l’évaluateur doit lui donner une note. Le problème est qu’un instant donné, plusieurs instances peuvent s’exécuter en parallèle. Lorsqu’un évaluateur donne une note, on doit affecter cette note au bon candidat. La solution pour trouver la bonne instance du workflow est la corrélation. Nous établirons une corrélation sur le nom du candidat. Ouvrez le workflow « EmbaucheSimpleService.xamlx » en mode design Remarquez que notre workflow contient deux activités « Receive ». Pour assurer une cohérence d’exécution, nous relierons les deux « Receive » par une corrélation. Le premier « Receive » doit initialiser une corrélation tandis que le deuxième doit s’appliquer à cette corrélation. Cliquez sur l’activité organigramme (FlowChart) parente Examinez les variables Remarquez la présence d’une variable appelée « __handle » de type « CorrlationHandle » Si cette variable n’existe pas, créez-la Changez la portée de la variable (Scope) pour qu’elle soit sur tout l’organigramme Renommez cette variable en « dossier » Double-cliquez sur la première séquence du workflow Cliquez sur la propriété « CorrelationInitializers » de l’activité « Receive » de la séquence Dans la zone « Add initializer », entrez « dossier » Dans la liste déroulante au dessus de la grille « XPath Queries », sélectionnez « Query Correlation Initializer » Dans la grille, dans la colonne « Query », sélectionnez la propriété « Nom » du paramètre « pInfo » Remarquez que VS génère automatiquement le chemin « XPath » relatif à la propriété Laissez la colonne « Key » à « Key1 » Cliquez sur « OK » Nous allons maintenant corréler la deuxième activité « Receive » avec la première Dans la deuxième activité « Receive » (Recevoir Evaluation), cliquez sur le bouton de la propriété « CorrelateOn » Dans la zone « Correlates With », entrez « dossier » Dans la « XPath Queries », sélectionnez le paramètre « Nom ». Remarquez que le chemin « XPath » est automatiquement généré. Ce que nous avons fait c’est que nous utilierons le paramètre « Nom » fourni par l’évaluateur pour trouver la bonne instance du workflow. Cliquez sur « OK » Le workflow est maintenant prêt. Compilez pour vérifier l’absence d’erreurs. Etape 12 : Exécution et Tests L’objectif de cette étape est de tester nos services. Nous validerons les trois cas de figures : Si le candidat a moins de 30 ans, sa candidature est automatiquement rejetée. Il reçoit immédiatement une notification. Si le candidat a plus de 30 ans, l’évaluateur doit l’évaluer. Lorsque l’évaluateur lui attribue une note supérieure ou égale à 3, il est accepté, sinon il est rejeté. Procédure : Lancez les trois applications Dans l’application « CandidatApp », entrez « Kamel » dans le nom et « 01/05/1986 » dans la date de naissance Cliquez sur « Soumettre » Remarquez que « Kamel » a été automatiquement rejeté Créez deux candidatures pour « Yazid » et pour « Racha » nés respectivement le « 25/03/1971 » et « 26/08/1978 » Remarquez que les candidatures n’ont pas été automatiquement rejetées Allez sur l’application « EvaluateurApp » Dans la zone « Nom » entrez « Racha » et sélectionnez « 4 » dans la note puis cliquez sur « Evaluer » Revenez à l’application « CandidatApp », remarquez qu’une notification indiquant que « Racha » a été acceptée. La corrélation a fait qu’on trouve le bon « Workflow » malgré que « Racha » a un dossier ultérieur à celui de « Yazid » Revenez à l’application « EvaluateurApp » Dans la zone « Nom » entrez « Yazid », dans la zone « Note » entrez « 2 » puis cliquez sur le bouton « Evaluer » Revenez sur l’application « CandidatApp » Remarquez une notification stipulant que « Yazid » a été rejetée       Le code source complet des applications de ce tutoriel est accessible ici. Enjoy !

Tutoriel 5.1 - Création d’un service de recrutement–Partie 1

L’objectif de ce tutoriel (cours 5) est de mettre en place un workflow de recrutement basé sur les services WCF. Pour ce, le workflow est hébergé dans une application console. Le candidat dispose d’une application qui lui permet de postuler à un poste et l’évaluateur a une application lui permettant d’évaluer une candidature. A la fin de l’évaluation, le candidat reçoit une notification de la note s’il a été accepté ou pas. Au début, si l’âge du candidat est inférieur à 30, sa candidature est systématiquement rejetée sans passer par l’évaluation. Etape 1 – Création de la solution. L’objectif est de créer une solution qui va contenir les différents modules du tutoriel. Lancez Visual Studio en tant qu’administrateur Créez une nouvelle solution vide appelée « Tutoriel51 » Ajoutez à la solution un projet de type « Application de service WCF Workflow » et appelez-le « EmbaucheLibrary » Remarquez que le projet contient un fichier dont l’extension est « xamlx » appelée « Service1.xamlx » Supprimez ce fichier Ajoutez au projet un nouvel élément de type « Service Workflow WCF » et appelez-le « EmbaucheSimpleService » Remarquez que par défaut, VS 2012 crée une séquence avec une activité « Receive » et une activité « Send » Supprimez la séquence et toutes les activités avec Etape 2 : Création d’un contrat de données L’objectif de cette étape est de créer un contrat de données (DataContract) qui représente des données qui vont être sérialisées et envoyées sur le réseau à travers les services WCF. Ajoutez une nouvelle classe au projet « EmbaucheSimpleService » appelée « CandidatInfo » Ajoutez deux propriétés publiques appelées «Nom » et « DateNaissance » de type « String » et « DateTime » public DateTime DateNaissance { get; set; } public string Nom { get; set; }   Ajoutez un « using » vers l’espace de nom « System.Runtime.Serialization » Décorez les deux propriétés avec l’attribut « DataMember » Décore la classe avec l’attriobut « DataContract » Compilez la solution pour vérifier l’absence d’erreurs. Le listing complet devrait être comme suit : [DataContract] public class CandidatInfo { [DataMember] public DateTime DateNaissance { get; set; } [DataMember] public string Nom { get; set; } }   Etape 3 : Création du service de candidature L’objectif de cette ��tape est de créer un service workflow qui permettra au candidat de présenter ses informations : nom et date de naissance. Ouvrez le workflow « EmbaucheSimpleService.xamlx » Glissez un organigramme sur le workflow Glissez une activité « ReceiveAndSendReply » et connectez-la au nœud de démarrage Cliquez sur l’organigramme parent Créez une variable « info » dont le type est « CandidatInfo » Créez une deuxième variable appelée « Resultat » de type « Boolean » Créez une troisième variable appelée « Note » de type « Int32 » Double-cliquez sur la séquence Renommez la première activité « Receive » en « Recevoir Candidature » Entrez « RecevoirCandidature » dans la zone « Nom de l’opération » Dans la propriété « ServiceContractName », entrez « IEmbaucheService » Cochez la propriété « CanCreateInstance » Cliquez sur « Contenu », la boîte de dialogue ci-dessous devrait apparaître Cliquez sur le bouton radio « Paramètres » Cliquez sur « Ajoutez nouveau paramètre » Dans la zone « Nom », entrez « pInfo » Pour le type, sélectionnez « CandidatInfo » Dans la zone « Assign To », entrez « info » Cliquez sur OK Glissez une activité de type « Assign » entre l’activité « Receive » et l’activité « Send » Dans la zone « To », entrez « Resultat » Dans la zone « Value » entrez l’expression suivante :info.DateNaissance < DateTime.Today.AddYears(-30) Ajoutez une activité « WriteLine » après « Assign » et avant « SendReplyToReceive » Affectez l’expression suivante à la propriété « Text »string.Format("Candidature reçue, nom : {0}, dn : {1}",info.Nom,info.DateNaissance) Dans l’activité « SendReplyToReceive », cliquez sur « Contenu » Dans la liste déroulante « Message Type », sélectionnez « Boolean » Dans la zone « Message Data », entrez « Resultat » La séquence devrait être comme suit : Compilez pour vérifier la présence d’erreurs. Etape 4 : Test du service L’objectif de cette étape est de tester le service en utilisant l’outil « WCFTestClient » fourni avec Visual Studio. Dans Visual Studio, dans l’explorateur de solution, cliquez avec le bouton droit sur le fichier « EmbaucheSimpleService.xamlx » Cliquez sur « Afficher sur le navigateur » Remarquez la définition du service s’affichant dans le navigateur.   Copiez l’adresse du service à partir de la barre d’adresse Lancez l’explorateur Windows Allez sur le répertoire d’installation de Visual Studio Allez sur le répertoire « Common7 à IDE » Exécutez l’outil « WCFTestClient.exe » Cliquez « Fichier à Ajouter Service » Dans la boîte de dialogue qui apparait, dans la zone de texte, collez l’URL copiée lors de l’étape 4 Remarquez la présence du service « IEmbaucheService » avec la méthode « RecevoirCandidature » Double-cliquez sur « RecevoirCandidature » Affectez des valeurs au paramètre « pInfo » (utiliser l’assistant qui propose des valeurs par défaut) Cliquez sur « Invoke » Remarquez que le service renvoie « True » pour les dates de naissance de plus de 30 ans et false sinon Etape 5 : Création de l’application d’hébergement en mode console Une des nombreuses façons d’héberger un workflow de service est une application de console. L’objectif de cette étape est d’utiliser la classe « WorkflowServiceHost » pour héberger un workflow. Ajoutez à la solution un nouveau projet de type « Application Console Workflow » et appelez-la « ConsoleWorkflowHost » A partir de l’explorateur de solution, faites de ce projet le projet de démarrage A partir de l’explorateur de solution, supprimer le workflow « Workflow1.xaml » créé avec le projet Ajoutez une référence vers « EmbaucheLibrary » Ouvrez le fichier « Program.cs » Supprimez le contenu de la méthode « Main » Ajoutez un using vers « System.ServiceModel.Activities » Ajoutez un using vers « System.Xaml » Ajoutez un using vers « System.ServiceModel.Description » La première étape consiste à charger le service à partir du fichier :// charger le service à partir du fichier WorkflowService service = XamlServices.Load(@"F:\FormationWF\Tutoriaux\Chapitre 5\Tutoriel51\EmbaucheLibrary\EmbaucheSimpleService.xamlx") as WorkflowService; Remplacez le chemin indiqué par le chemin dans lequel se trouve votre code source La deuxième étape consiste à compiler le service :// compiler le service Compile(service); Nous fournirons l’implémentation de la méthode « Compile » dans une prochaine étape La prochaine étape consiste à définir l’adresse du service :// définir l'adresse du service Uri address = new Uri("http://localhost:9988/EmbaucheSimple"); Ensuite, nous lancerons l’hôte en utilisant le workflow et l’adresse :// créer le hôte WorkflowServiceHost host = new WorkflowServiceHost(service, address); Nous paramétrerons le service de façon à ce qu’il communique ses métadonnées via HTTP :// communiquer métadonnées via HTTP host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); Nous supprimerons ensuite le comportement de débogage du service// supprimer le comportement de débogage host.Description.Behaviors.Remove(typeof(ServiceDebugBehavior)); Nous ajouterons ensuite le comportement qui inclut les informations des exceptions avec les messages. Ce comportement est très utile pour le débogage.// inclure les détails des exception dans les messages host.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true }); Maintenant que l’hôte est configué, il faut le lancer// démarrer le service host.Open(); Ajoutons une instruction qui lit une touche à partir du clavier (Console.ReadKey()) et afficher un message avantConsole.WriteLine("Service démarré"); Console.ReadKey(); Le listing complet de la méthode « Main » devraît être comme suit : static void Main(string[] args) { // charger le service à partir du fichier WorkflowService service = XamlServices.Load(@"F:\FormationWF\Tutoriaux\Chapitre 5\Tutoriel51\EmbaucheLibrary\EmbaucheSimpleService.xamlx") as WorkflowService; // compiler le service Compile(service); // définir l'adresse du service Uri address = new Uri("http://localhost:9988/EmbaucheSimple"); // créer le hôte WorkflowServiceHost host = new WorkflowServiceHost(service, address); // communiquer métadonnées via HTTP host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); // supprimer le comportement de débogage host.Description.Behaviors.Remove(typeof(ServiceDebugBehavior)); // inclure les détails des exception dans les messages host.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true }); // démarrer le service host.Open(); Console.WriteLine("Service démarré"); Console.ReadKey(); } Ajoutez un using vers « System.Activities.XamlIntegration » Ajoutez un using vers « System.Activities.Expressions » Ajoutez la méthode « Compile » comme suit : static void Compile(WorkflowService workflowService) { TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings { Activity = workflowService.Body, Language = "C#", ActivityName = workflowService.Name + "_CompiledExpressionRoot", ActivityNamespace = string.Join(".", "CompiledExpressions"), RootNamespace = null, GenerateAsPartialClass = false, ForImplementation = false, // Important tip - ForImplementation should only be true when compiling an ActivityBuilder, it must be false when compiling an Activity such as a WorfkflowService body. };  TextExpressionCompilerResults results = new TextExpressionCompiler(settings).Compile();  if (results.HasErrors) { throw new Exception("Compilation failed."); }  ICompiledExpressionRoot compiledExpressionRoot = Activator.CreateInstance(results.ResultType, new object[] { workflowService.Body }) as ICompiledExpressionRoot;  CompiledExpressionInvoker.SetCompiledExpressionRoot( workflowService.Body, compiledExpressionRoot); }   L’implémentation de « Compile a été tirée du service de « Microsoft » (https://connect.microsoft.com/VisualStudio/feedback/details/743870/workflow-foundation-4-5-workflowservicehost-xamlx-expression-activity-type-csharpvalue1-requires-compilation-in-order-to-run ) Etape 6 : Création du service d’évaluation L’objectif de cette étape est d’ajouter le service permettant à la commission technique d’évaluer un candidat. Ouvrez le workflow « EmbaucheSimpleService.xaml » Ouvrez l’organigramme principal qui contient la séquence Ajoutez une activité « FlowDecision » et connectez-la à la séquence Entrez « Resultat » dans l’expression de la condition de la décision Ajoutez une activité « Receive » et appelez-la « Recevoir Evaluation » Connectez l’activité au label « True » de la décision Entrez « IEmbaucheService » dans la propriété « ServiceContractName » Entrez « Evaluer » dans la zone « Nom de l’opération » Cliquez sur le bouton « Contenu » de l’activité « Receive » Cliquez sur le bouton radio « Paramètres » Ajoutez un paramètre appelé « nom » de type « String » Ajoutez un paramètre appelé « note » de type « Int32 » affectée à la variable « Note » Cliquez sur « OK » Ajoutez une activité « Assign » et connectez-la au dernier « Receive » Dans la zone « To » entrez « Resultat » Dans la zone expression, entrez « Note >=3) Le workflow devrait être comme suit : Compilez pour vérifier les erreurs. Vous pouvez accéder à la suite du tutoriel ici.

WCF et IIS 8.5, Régler le problème de l’erreur 404.17

Après quelques recherches internet et bien sûr, des maux de tête, j’ai réussi à débloquer le problème d’erreur 404.17 faisant suite à l’hébergement d’un service WCF sur IIS 8.5. Sachant que j’utilise IIS 8.5 sous Windows 8.1, la solution est relativement simple : Lancer le panneau de configuration Lancer “Programmes et Fonctionnalités” Activer ou Désactiver des Fonctionnalités Windows” Services .NET Framewok 4.5 avancé Service WCF Activation HTTP                   Le problème devrait être logiquement résolu

Tutoriel 7.1: Intégration d’AJAX dans les applications web

L’objectif de ce tutoriel (associé au module 7) est de montrer comment intégrer les technologies AJAX dans les applications ASP.NET. La première partie, utilise les dates pour montrer comment UpdatePanel permet uniquement à des portions de page d’être actualisées. La deuxième partie consiste en la création d’un service web et son invocation depuis le client en utilisant JavaScript et JQuery. Prérequis : Ce tutoriel requiert que la base de données « AdventureWorks » soit installée dans la machine. Cette base peut être téléchargée sur http://sqlserversamples.codeplex.com/ . Etape 1 – Préparation L’objectif de cette étape est de préparer l’application. Créer une nouvelle application ASP.NET vide appelée « TestAJAX » Créez trois pages dans la nouvelle application « Default.aspx », « Partiel.aspx » et « Service.aspx » Dans la page web « Default.aspx » ajoutez deux liens pointant respectivement sur « Partiel.aspx » et « Service.aspx » avec la propriété « Text » égale à : « Chargement Partiel » et « Service Web » respectivement. Ajoutez un modèle EntityFramework appelé « AdventureModel » pointant sur la base de données « AdventureWorks ». Le nom du contexte doit être « AdventureContext ». Pour plus de détails voir « Module 6 ». N’oubliez pas de cochez « Mettre au plusieurs ou au singulier les entités générées ». Le modèle inclut une seule table « Product » et génère une seule entité « Product » Ajoutez une feuille de style appelée « Site.css » Ajoutez les règles suivantes à la feuille de style : body { font-family: 'Gill Sans' , 'Gill Sans MT' , Calibri, 'Trebuchet MS' , sans-serif; } .asideBar { border: thin solid #919191; float: left; margin: 5px; background-color: #F8E9AD; padding : 5px; min-width : 200px; min-height : 400px; }   div.content { border: thin solid #0D86FF; float: left; margin: 5px; padding: 5px; }   div.clear { float : left; }   .loading { text-align: center; background-color: #C24B4B; color: #FFFFFF; margin-top: 2px; padding: 3px; } Ouvrez la page « Partiel.aspx » en mode source Insérez les balises suivantes après la balise « div » du formulaire (« form ») : <div> <aside class="asideBar"> </aside> <div class="content"> </div> <div class="clear" /> </div> Ouvrez la page « Partiel.aspx » en mode design Faites glisser le fichier « Site.css » sur la page afin d’appliquer le style. Ouvrez la console du gestionnaire de paquets « Nuget » Installez « JQuery » en entrant « Install-Package JQuery » Etape 2 : Implémentation de chargement partiel L’objectif de cette étape de permettre au développeur de voir l’effet du chargement partiel en comparant entre le contenu de deux libellés. Elle montre aussi l’utilisation de l’UpdatePanel en conjonction avec l’UpdateProgress. Ouvrez la page « Partiel.aspx » en mode design Cliquez sur la barre latérale « aside » puis insérer un « Label » dedans A partir de la barre à outils, ajoutez un « ScriptManager » (onglet extensions AJAX) en début de page Ajoutez une source de données « EntityDataSource » et pointez-la sur l’ensemble « Products » du contexte « AdventureContext ». Si vous n’arrivez pas à voir le contexte, compilez la solution en appuyant sur « F6 » Ajoutez un « UpdatePanel » dans la « div » dont la classe est « content » Insérez un deuxième « Label » à l’intérieur de l’UpdatePanel que vous venez d’ajouter Ajoutez un « GridView » à l’intérieur du composant « UpdatePanel » que vous venez d’ajouter. Configurez la source de données pour pointer sur la source de données « EntityDataSource » ajoutée précédemment. Configurez le « GridView » de façon à ce qu’il ne contienne que les colonnes « ProductID », « Name » et « Color » Configurez la pagination du GridView en affectant « True » à la propriété « AllowPaging » Ajoutez un composant « UpdateProgress » en dessous du « UpdatePanel » Dans la propriété « DisplayAfter », entrez 50 Dans la propriété « AssociatedUpdatepanelID » entrez « UpdatePanel1 » Affichez la page « Partiel.aspx » en mode source Dans la propriété « ProgressTemplate » de l’UpdateProgress insérez un div et du texte comme suit : <asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="50" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> <div class="loading">Chargement en cours...</div> </ProgressTemplate> </asp:UpdateProgress> La page en mode design doit être comme suit :   Le code ASPX généré doit être comme celui-ci : <form id="form1" runat="server"> <div>   <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:EntityDataSource ID="EntityDataSource1" runat="server" ConnectionString="name=AdventureContext" DefaultContainerName="AdventureContext" EnableFlattening="False" EntitySetName="Products"> </asp:EntityDataSource> <aside class="asideBar"> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </aside> <div class="content"> <asp:UpdatePanel ID="UpdatePanel1" runat="server" AssociatedUpdatePanelID="UpdatePanel1" UpdateMode="Conditional"> <ContentTemplate> <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label> <br /> <asp:GridView ID="GridView1" runat="server" BackColor="LightGoldenrodYellow" BorderColor="Tan" BorderWidth="1px" CellPadding="2" ForeColor="Black" GridLines="None" AllowPaging="True" AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="EntityDataSource1"> <AlternatingRowStyle BackColor="PaleGoldenrod" /> <Columns> <asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID" /> <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" /> <asp:BoundField DataField="Color" HeaderText="Color" SortExpression="Color" /> </Columns> <FooterStyle BackColor="Tan" /> <HeaderStyle BackColor="Tan" Font-Bold="True" /> <PagerStyle BackColor="PaleGoldenrod" ForeColor="DarkSlateBlue" HorizontalAlign="Center" /> <SelectedRowStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" /> <SortedAscendingCellStyle BackColor="#FAFAE7" /> <SortedAscendingHeaderStyle BackColor="#DAC09E" /> <SortedDescendingCellStyle BackColor="#E1DB9C" /> <SortedDescendingHeaderStyle BackColor="#C2A47B" /> </asp:GridView> </ContentTemplate> </asp:UpdatePanel>   <asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="50" AssociatedUpdatePanelID="UpdatePanel1"> <ProgressTemplate> <div class="loading">Chargement en cours...</div> </ProgressTemplate> </asp:UpdateProgress>   </div> <div class="clear" />   </div> </form> Passez en mode « Code Behind » en appuyant sur « F7 » Dans la méthode « Page_Load », affectez la date en cours aux deux libellés comme suit : protected void Page_Load(object sender, EventArgs e) { Label1.Text = DateTime.Now.ToString(); Label2.Text = DateTime.Now.ToString(); } Exécutez en appuyant sur « F5 » et naviguez jusqu’à la page « Partiel.aspx » Remarquez qu’en changeant la page en cours dans la grille, la date ne change que pour un seul libellé alors que la page devait changer les deux Remarquez que le « UpdateProgress » s’affiche à chaque fois qu’un lien de pagination est cliqué Etape 3 : Création d’un service web WCF Dans le projet « TestAJAX », cliquez sur « Ajouter un Nouvel Elément » Dans le modèle sélectionnez « Service WCF AJAX » Dans la zone « Nom », entrez « ProductService » Appuyez sur « OK » Remarquez que VS génère une classe de service avec une seule méthode « DoWork » comme suit : [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class ProductService { // To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json) // To create an operation that returns XML, // add [WebGet(ResponseFormat=WebMessageFormat.Xml)], // and include the following line in the operation body: // WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml"; [OperationContract] public void DoWork() { // Add your operation implementation here return; }   // Add more operations here and mark them with [OperationContract] } Supprimez la méthode « DoWork » de la classe Ajoutez une méthode « GetProduct » décorée par l’attribut « OperationContract » L’attribut « OperationContract » stipule que la méthode peut être appelée comme service Changez le code de la méthode comme suit :   [OperationContract] public Product GetProduct(int id) { using (var context = new AdventureContext()) { return context.Products.FirstOrDefault(e => e.ProductID == id); } } Compilez en appuyant sur « F6 » et vérifiez qu’il n’y a pas d’erreur Ouvrez la page « Default.aspx » Exécutez en appuyant sur « F5 » Dans la barre d’adresse du navigateur, remplacez « Default.aspx » par « ProductService.svc » Remarquez les informations données sur le service permettant aux clients de l’invoquer. Etape 4 : Invocation du service web depuis JavaScript Le but de cette étape est d’utiliser le composant « ScriptManager » afin qu’il génère les proxies nécessaires à l’invocation du service « ProductService ». Pour invoquer le services, des évènements JQuery seront utilisés. Ouvrez la page « Service.aspx » en mode design Glissez un « ScriptManager » sur la page Cliquez sur la propriété « Services » du « ScriptManager » puis sur le bouton en pointillés qui apparaît Cliquez sur le bouton « Ajouter » A droite, dans la propriété « Path », entrez « ~/ProductService.svc » Cliquez sur « OK » Ouvrez la page « Service.aspx » en mode source Ajoutez trois paragraphes (« <p> ») dont les deux derniers ont l’attribut « class » à « info » Dans le premier paragraphe, ajoutez une balise « label » contenant le texte « Numéro : » Dans le premier paragraphe, ajoutez un contrôle HTML (pas ASP.NET) de type bouton avec la propriété « ID » pour « btnChercher » et l’attribut « value » à « Trouver Produit » Dans le deuxième paragraphe, ajoutez une balise « label » avec le texte « Nom : » Dans le deuxième paragraphe, ajoutez un contrôle HTML « input (Text ») avec la propriété « ID » à « txtNom » Dans le troisième paragraphe ajoutez une balise « label » avec le texte « Couleur : » Dans le deuxième paragraphe, ajoutez un contrôle HTML « input (Text ») avec la propriété « ID » à « txtNom » Le code devra ressembler au code qui suit : <form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> <Services> <asp:ServiceReference Path="~/ProductService.svc" /> </Services> </asp:ScriptManager> <p> <label> Trouver : <input id="txtNumero" type="text" /></label> <input id="btnChercher" type="button" value="Trouver Produit" /> </p> <p class="info"> <label> Nom : </label> <input id="txtNom" type="text" />   </p> <p class="info"> <label> Couleur : </label> <input id="txtCouleur" type="text" /> </p> </div> En mode source toujours, faites glisser le fichier « JQuey-x.y.z.js » dans la page juste avant la balise «/head » fermante où x.y.z est la version installée de JQuery En mode source, ajoutez le script « JavaScript » suivant : <script type="text/javascript"> $(function () { $(".info").hide(); $("#btnChercher").click(function () { var numero = $("#txtNumero").val(); ProductService.GetProduct(numero, function (data) { if (data == null) { alert('non trouvé !'); $(".info").hide(); } else { $("#txtNom").val(data.Name); $("#txtCouleur").val(data.Color); $(".info").show(); } }); }); }); </script> La ligne suivante permet d’invoquer l’opération « GetService » du service « GetProduct » avec comme paramètre la variable « numero ». Le deuxième paramètre est la fonction à appeler lorsque l’appel réussit. AJAX est bâti sur ce genre d’appel car le services s’exécutent d’une manière asynchrone, la fonction passée en paramètre est appelée « callback ». ProductService.GetProduct(numero, function (data) { if (data == null) { alert('non trouvé !'); $(".info").hide(); } else { $("#txtNom").val(data.Name); $("#txtCouleur").val(data.Color); $(".info").show(); } }); Exécutez l’application en appuyant sur « F5 » Pour télécharger le code du tutoriel, cliquez ici