none
WPF - WCF traitement asynchrone RRS feed

  • Question

  • Bonjour

    voilà j'ai une application WPF qui se connecte a des services WCF... 
    J'ai la main sur le code du WCF et du WPF.

    Pour mes traitements j'ai le choix entre :
    • des services synchrones avec un backgroundworker...
    • des services asynchrones avec le traitement qui va avec...

    Quelle méthodes pensez vous être la meilleurs ? 

    Auriez vous des tutos pour du WPF MVVM avec un écran du style "veuillez patienter !" pour les traitements ?

    Merci de votre aide
    mercredi 13 juin 2012 15:13

Réponses

  • Bonjour,

    Si vous utilisez les tâches, préférez l'utilisation d'un proxy synchrone.
    L'utilisation d'un proxy asynchronse (basé sur les événements) n'est pas si simple à coder. Et si vous devez exécuter une série d'opération synchrone sur votre webservice (tout en étant asynchrone au niveau de l'IHM), cela sera difficile à réaliser.

    Votre approche avec une propriété IsBusy est correct dans le cadre du pattern MVVM.

    Cordialement


    Gilles TOURREAU - MVP C#
    Architecte logiciel/Consultant/Formateur Freelance
    Blog : http://gilles.tourreau.fr
    - MCPD : Enterprise Developper / Windows Developper 3.5 / ASP .NET 3.5/4.0
    - MCITP : SQL Server 2008 Developper
    - MCTS : ADO .NET 3.5 / SQL Server 2008 Developper / Windows Forms 3.5 / ASP .NET 3.5/4.0

    dimanche 17 juin 2012 14:41
    Modérateur
  • Bonjour,

    j'ai finalement opté pour les Task du 4.0. avec des méthodes synchrones, très rapide à mettre en place.

    this.IsBusy = true;
    Task saveTask = new Task(() =>
                    {
                        this.Media = MediaService.Save(this.Media);
                    });
    
                saveTask.ContinueWith(t =>
                    {
                            this.UpdateMedia();
                            this.IsBusy = false;
                    }, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());

    Cependant, j'ai un souci avec le "TaskScheduler.FromCurrentSynchronizationContext()" en effet si dans mon "UpdateMedia" je veux relancer une autre Task (qui va aussi mettre à jour l'UI), je ne peux utiliser "TaskScheduler.FromCurrentSynchronizationContext()" dans le "continueWith" de cette nouvelle Task. je dois passer alors utiliser "TaskScheduler.Current".

    Mais si je veux appeler directement la méthode "UpdateMedia" (lors d'un clique sur un bouton par exemple), là je dois remettre "TaskScheduler.FromCurrentSynchronizationContext()".. il faut donc jongler avec le scheduler, ce que je trouve un peu lourd.

    Je trouve qu'il manque une méthode du style "saveTask.Completed(t => { ...}) qui serai exécuter, lorsque la tache est terminée, dans le thread principal (à la maniere du OnRunWorkerCompleted du BackgroungWorker) pour si besoin relancer d'autre Task depuis le thread principal.



    mardi 26 juin 2012 07:25

Toutes les réponses

  • Bonjour,

    De manière générale, faire des appels en webservice de façon asynchrone est une bonne pratique il me semble, cela évite de geler l'affichage à cause d'une requete qui met trop de temps à arriver.

    C'est aussi plus contraignant dans la mesure ou il faut gérer ces états temporaires ou on est en attente du resultat d'une requete.

    Si ce sont des cas isolés ou si vous choisissez des proxies synchrones, le background worker est un des moyens possibles pour rendre certains appels asynchrones.
    (un petit topic sur le sujet: http://stackoverflow.com/questions/5327096/is-backgroundworker-the-only-way-to-keep-a-wcf-wpf-application-responsive)
    Petit avantage du BackgroundWorker ceci dit, c'est qu'il a un status (isBusy par exemple) qui peut etre utilisé directement dans la vue pour afficher un loader ou autres informations

    Il me semble que si vous êtes sûr de vouloir faire tous vos appels en asynchrone, la meilleur solution consisterait à générer un proxy qui fait directement des appels asynchrones.
    J'avais fait (il y a déjà un long moment...) un article sur une facon de packager les appels des webservices asynchrones qui a une pertinence relative mais qui peut donner des idées éventuellement:
    http://charly-studio.com/blog/mvvmwcf-pattern-silverlight/

    Un autre article sur la msdn concernant les threading en WPF:
    http://msdn.microsoft.com/en-us/library/ms741870.aspx

    en esperant vous avoir aidé dans vos recherches

    Cordialement,


    Charles HETIER http://www.charly-studio.com

    mercredi 13 juin 2012 17:50
  • Bonjour,

    Si vous êtes sous .NET Framework 4.0, vous pouvez aussi utiliser les Task pour déclencher un traintement asynchrone et une autre tâche qui s'exécute à fin de votre traitement afin de mettre à jour l'interface graphique.

    C'est selon moi la solution la simple (en attendant les mots-clé async/await du 4.5) pour l'exécution de traitement asynchrone avec une interface graphique.

    Cordialement


    Gilles TOURREAU - MVP C#
    Architecte logiciel/Consultant/Formateur Freelance
    Blog : http://gilles.tourreau.fr
    - MCPD : Enterprise Developper / Windows Developper 3.5 / ASP .NET 3.5/4.0
    - MCITP : SQL Server 2008 Developper
    - MCTS : ADO .NET 3.5 / SQL Server 2008 Developper / Windows Forms 3.5 / ASP .NET 3.5/4.0

    jeudi 14 juin 2012 07:33
    Modérateur
  • Bonjour et merci de votre réponse,

    J'aime bien l'idée d'utiliser les "Task" et ça convient bien au MVVM je pense. Dois je quand même générer mon proxy en asynchrone ? ou dois je utiliser les méthodes synchrones ?

    De plus , pour mon interface, j'imaginais un UserControle qui s'affiche en plein milieu de l'écran lors d'un chargement (du style "Chargement en cours" et qui empêche les modifications à l'écran). L'affichage de ce UserControl serait "bindée" sur une variable static (par exemple Application.Current.IsBusy").

    Et dans mon code, avant d'appeler mon service, je fais un "Application.Current.IsBusy = True" et une fois la réponse obtenue, je fais un "Application.Current.IsBusy = False".

    Pensez vous que cette approche soit bonne ?


    jeudi 14 juin 2012 09:45
  • Bonjour,

    Si vous utilisez les tâches, préférez l'utilisation d'un proxy synchrone.
    L'utilisation d'un proxy asynchronse (basé sur les événements) n'est pas si simple à coder. Et si vous devez exécuter une série d'opération synchrone sur votre webservice (tout en étant asynchrone au niveau de l'IHM), cela sera difficile à réaliser.

    Votre approche avec une propriété IsBusy est correct dans le cadre du pattern MVVM.

    Cordialement


    Gilles TOURREAU - MVP C#
    Architecte logiciel/Consultant/Formateur Freelance
    Blog : http://gilles.tourreau.fr
    - MCPD : Enterprise Developper / Windows Developper 3.5 / ASP .NET 3.5/4.0
    - MCITP : SQL Server 2008 Developper
    - MCTS : ADO .NET 3.5 / SQL Server 2008 Developper / Windows Forms 3.5 / ASP .NET 3.5/4.0

    dimanche 17 juin 2012 14:41
    Modérateur
  • Vous pouvez utiliser le pattern asynchrone généré pour les services WCF avec les Task. Il y a une méthode de Task qui est faite pour ca exactement (me souviens plus du nom de tête mais il attend le Beginxxx et le Endxxx comme arguments).

    Richard Clark
    Consultant - Formateur .NET
    http://www.c2i.fr
    Depuis 1996: le 1er site .NET francophone

    lundi 25 juin 2012 22:14
  • Bonjour,

    Est-ce que vous avez testé la solution proposée ? Merci de partager avec nous les résultats,afin que d'autres personnes avec le même problème puissent profiter de cette solution.


    Cordialement, Pascal.

    Développeur Wpf/SilverLight/WinPhone7

    mardi 26 juin 2012 04:47
    Modérateur
  • Bonjour,

    j'ai finalement opté pour les Task du 4.0. avec des méthodes synchrones, très rapide à mettre en place.

    this.IsBusy = true;
    Task saveTask = new Task(() =>
                    {
                        this.Media = MediaService.Save(this.Media);
                    });
    
                saveTask.ContinueWith(t =>
                    {
                            this.UpdateMedia();
                            this.IsBusy = false;
                    }, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());

    Cependant, j'ai un souci avec le "TaskScheduler.FromCurrentSynchronizationContext()" en effet si dans mon "UpdateMedia" je veux relancer une autre Task (qui va aussi mettre à jour l'UI), je ne peux utiliser "TaskScheduler.FromCurrentSynchronizationContext()" dans le "continueWith" de cette nouvelle Task. je dois passer alors utiliser "TaskScheduler.Current".

    Mais si je veux appeler directement la méthode "UpdateMedia" (lors d'un clique sur un bouton par exemple), là je dois remettre "TaskScheduler.FromCurrentSynchronizationContext()".. il faut donc jongler avec le scheduler, ce que je trouve un peu lourd.

    Je trouve qu'il manque une méthode du style "saveTask.Completed(t => { ...}) qui serai exécuter, lorsque la tache est terminée, dans le thread principal (à la maniere du OnRunWorkerCompleted du BackgroungWorker) pour si besoin relancer d'autre Task depuis le thread principal.



    mardi 26 juin 2012 07:25