2010-11-19 7 views
0

Entfernen:die Event-Handler auf wcf Service-Aufruf

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); 
     } 
); 

}

Das Problem ist, ich eine Taste, die diesen Code aus abfeuert. Wenn der Knopf mehr als einmal gedrückt wird, wird der Handler immer wieder hinzugefügt. Dies ist ein Problem, da der Code so oft ausgelöst wird, wie der Benutzer die Schaltfläche gedrückt hat. Wie kann ich den mit dem Lambda-Ausdruck erstellten Handler in diesem Code entfernen, sodass er nur einmal ausgeführt wird?

Danke!

EDIT:

ich den Code wie folgt aus meinem Button-Klick-Befehl aufrufen:

  _dataService.GetData(GetDataCompleted); 

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

I Ich würde vorschlagen, diesen Codeblock zu reparieren, die Hälfte davon ist nicht als Code markiert. Verwenden Sie das Symbol 010101, nachdem Sie Ihren Codeblock markiert haben. –

+0

Können Sie den Code hinzufügen, in dem sich der Button-Klick-Handler befindet? Es gibt hier nicht genug, um zu bestimmen, warum dieser Handler viele Male hinzugefügt wird. –

+0

Ich weiß nicht, was Sie meinen, es sieht so aus, als ob der Code in den Code-Blöcken für mich richtig angezeigt wird. – adminJaxon

Antwort

0

Ich denke, dass Sie es durch die Implementierung eines Push-Pull-Strategie in Ihrem Code-behind lösen . Ich schlage vor, etwas Ähnliches wie folgt aus:

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; 
     } 
    }   
} 

Noch besser, die UI-Taste deaktivieren, wenn Sie eine ausstehende Anforderung haben. Sie hätten keine Nebenläufigkeitsprobleme beim Zugriff auf _requestPending von verschiedenen Threads und können diese ändern, aber trotzdem könnten Sie eine Race-Bedingung erleiden, wenn die Service-Antwort schnell genug ist, also noch besser, um die beiden Code-Blöcke zu synchronisieren.

Wie auch immer, persönlich mag ich diese Implementierung nicht für das, was Sie erreichen wollen. Der Code ist ziemlich verwirrend und macht es schwierig, mögliche Probleme vorherzusehen. Stellen Sie sicher, dass:

  • Sie bieten eine Möglichkeit, eine Anfrage und reaktivieren Sie die Taste erneut

  • der Code, der den Bildschirm aktualisiert abbrechen durch den UI-Thread ausgeführt wird