拆卸WCF服务呼叫
问题描述:
事件处理程序我最近遇到了这个代码:拆卸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 }
答
我认为,你可以通过在实施推拉战略代码隐藏解决问题。我提出一些与此类似:
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线程
我执行的代码'd建议修复该代码块,其中一半未标记为代码。突出显示代码块后使用010101图标。 – 2010-11-19 21:16:00
你可以添加按钮点击处理程序的实际代码吗?这里没有足够的东西来确定为什么这个处理程序被添加了很多次。 – 2010-11-19 21:36:46
我不知道你的意思,它看起来像代码正在代码块内正确显示给我。 – adminJaxon 2010-11-20 17:37:08