2010-11-19 3 views
0

Je suis venu récemment dans ce code:Suppression du gestionnaire d'événements sur appel de service WCF

public static class ClientBaseExtender 
{ 
    /// <summary> 
    /// Tries to execute async service call. If <see cref="TimeoutException"/> occured retries again. 
    /// </summary> 
    /// <typeparam name="TChannel">ServiceClient class.</typeparam> 
    /// <typeparam name="TArgs">Type of service client method return argument.</typeparam> 
    /// <param name="client">ServiceClient instance.</param> 
    /// <param name="tryExecute">Delegate that execute starting of service call.</param> 
    /// <param name="onCompletedSubcribe">Delegate that subcribes an event handler to the OnCompleted event of the service client method.</param> 
    /// <param name="onCompleted">Delegate that executes when service call is succeeded.</param> 
    /// <param name="onError">Delegate that executes when service call fails.</param> 
    /// <param name="maxAttempts">Maximum attempts to execute service call before error if <see cref="TimeoutException"/> occured (by default 5).</param> 
    public static void ExecuteAsyncRepeatedly<TChannel, TArgs>(this ClientBase<TChannel> client, Action tryExecute, 
                   Action<EventHandler<TArgs>> onCompletedSubcribe, EventHandler<TArgs> onCompleted, 
                   EventHandler<TArgs> onError, int maxAttempts) 
     where TChannel : class 
     where TArgs : AsyncCompletedEventArgs 
    { 
     int attempts = 0; 
     var serviceName = client.GetType().Name; 

     onCompletedSubcribe((s, e) => 
           { 
            if (e.Error == null) // Everything is OK 
            { 
             if (onCompleted != null) 
              onCompleted(s, e); 

             ((ICommunicationObject)client).Close(); 
             Debug.WriteLine("[{1}] Service '{0}' closed.", serviceName, DateTime.Now); 
            } 
            else if (e.Error is TimeoutException) 
            { 
             attempts++; 

             if (attempts >= maxAttempts) // Final timeout after n attempts 
             { 
              Debug.WriteLine("[{2}], Final Timeout occured in '{0}' service after {1} attempts.", serviceName, attempts, DateTime.Now); 

              if (onError != null) 
               onError(s, e); 
              client.Abort(); 

              Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now); 
              return; 
             } 

             // Local timeout 
             Debug.WriteLine("[{2}] Timeout occured in '{0}' service (attempt #{1}).", serviceName, attempts, DateTime.Now); 

             Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now); 
             tryExecute(); // Try again. 
            } 
            else 
            { 
             if (onError != null) 
              onError(s, e); 
             client.Abort(); 
             Debug.WriteLine("[{1}] Service '{0}' aborted.", serviceName, DateTime.Now); 
            } 
           }); 

     Debug.WriteLine("[{2}] Attempt #{0} to execute call to '{1}' service.", attempts + 1, serviceName, DateTime.Now); 
     tryExecute(); // First attempt to execute 
    } 
} 

    public void GetData() 
    { 
    var client = new MyServiceClient(); 
    client.ExecuteAsyncRepeatedly(() => client.MyOperationAsync(...), 
    (EventHandler<MyOperationCompletedEventArgs> handler)          =>client.MyOperationCompleted += handler, 
    (s, e) => // OnCompleted 
     { 
      Do(e.Result); 
     }, 
    (s, e) => // OnError 
     { 
      HandleError(e.Error); 
     } 
); 

}

Le problème est, j'ai un bouton qui déclenche ce code off. Lorsque le bouton est enfoncé plusieurs fois, le gestionnaire est ajouté à plusieurs reprises. C'est un problème car le code se déclenchera autant de fois que l'utilisateur a appuyé sur le bouton. Comment puis-je supprimer le gestionnaire créé avec l'expression lambda dans ce code pour qu'il ne s'exécute qu'une seule fois?

Merci!

EDIT:

J'appelle le code comme ceci de mon bouton de commande de clic:

  _dataService.GetData(GetDataCompleted); 

     private void GetDataComplete(Data data) 
    { 
     //do something with data  } 
+0

I Suggère de réparer ce bloc de code, la moitié n'est pas marquée comme du code. Utilisez l'icône 010101 après avoir sélectionné votre bloc de code. –

+0

Pouvez-vous ajouter dans le code réel où le gestionnaire de clic de bouton est? Il n'y a pas assez ici pour déterminer pourquoi ce gestionnaire est ajouté plusieurs fois. –

+0

Je ne sais pas ce que vous voulez dire, il semble que le code s'affiche correctement dans les blocs de code pour moi. – adminJaxon

Répondre

0

Je pense que vous pouvez le résoudre en mettant en œuvre une stratégie push-pull dans votre code-behind . Je propose quelque chose de similaire à ceci:

bool _requestPending; 
readonly object _lock = new object(); 

void OnClick (...) 
{ 
    lock(_lock) 
    { 
     if (_requestPending == false) 
     { 
      _dataService.GetData(GetDataCompleted); 
      _requestPending = true; 
     } 
    } 
} 
private void GetDataComplete(Data data) 
{ 
    lock(_lock) 
    { 
     try 
     { 
      //do something with data 
     } 
     finally 
     { 
      _requestPending = false; 
     } 
    }   
} 

Mieux encore, désactiver le bouton de l'interface utilisateur lorsque vous avez une demande en attente. Vous n'auriez aucun problème de concurrence avec l'accès et la modification du _requestPending de différents threads, mais vous pourriez toujours souffrir d'une condition de concurrence si la réponse du service est assez rapide, donc mieux de synchroniser les deux blocs de code.

De toute façon, personnellement, je n'aime pas cette implémentation pour ce que vous essayez d'atteindre. Le code est assez déroutant, et il est difficile de prévoir les problèmes qui peuvent survenir. Assurez-vous que:

  • vous fournir un moyen d'annuler une demande et réactiver le nouveau sur le bouton

  • le code qui met à jour l'écran est exécuté par le thread d'interface utilisateur

Questions connexes