2010-11-19 152 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线程