2010-11-19 150 views
0

事件處理程序我最近遇到了這個代碼:拆卸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); 
     } 
); 

}

的問題是,我有一個按鈕,關閉觸發此代碼。當多次按下按鈕時,處理程序會一次又一次地被添加。這是一個問題,因爲代碼會觸發用戶按下按鈕的次數。我該如何刪除在這段代碼中使用lambda表達式創建的處理程序,以便它只運行一次?

謝謝!

編輯:

我從我的按鈕單擊命令調用這樣的代碼:

  _dataService.GetData(GetDataCompleted); 

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

我執行的代碼'd建議修復該代碼塊,其中一半未標記爲代碼。突出顯示代碼塊後使用010101圖標。 – 2010-11-19 21:16:00

+0

你可以添加按鈕點擊處理程序的實際代碼嗎?這裏沒有足夠的東西來確定爲什麼這個處理程序被添加了很多次。 – 2010-11-19 21:36:46

+0

我不知道你的意思,它看起來像代碼正在代碼塊內正確顯示給我。 – adminJaxon 2010-11-20 17:37:08

回答

0

我認爲,你可以通過在實施推拉戰略代碼隱藏解決問題。我提出一些與此類似:

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

更妙的是,禁用UI按鈕,當你有一個掛起的請求。從不同的線程訪問和修改_requestPending並不會有任何併發​​問題,但是如果服務響應足夠快,您仍然可能遭受競爭狀態,因此更好地同步這兩個代碼塊。

無論如何,我個人不喜歡這個實現你想要實現的。代碼很混亂,並且很難預見可能出現的問題。確保:

  • 您提供一種方式來中止請求 ,並再次重新啓用按鈕

  • ,更新屏幕 由UI線程