需要帮助在c# 4.0 Web服务客户端中实现异步调用
本文关键字:客户端 服务 实现 调用 异步 Web 帮助 | 更新日期: 2023-09-27 18:16:28
所以我正在开发一个使用web服务的客户端。我使用来自服务的WSDL和XSD文件来生成代理类,所有同步函数都运行良好。然而,考虑到它们的同步特性,进行任何调用都会导致UI停止响应,直到调用完成。使用异步方法的经典原因,对吧?
问题是,我还在学校攻读学位,对异步编程知之甚少。我试着在网上查阅相关资料(我的雇主甚至有一个Books全天候订阅服务),但我很难掌握应该如何打电话以及如何处理回应。我有:
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://localhost:8080/getRecords", RequestNamespace="http://www.<redacted>.com/ws/schemas", ResponseNamespace="http://www.<redacted>.com/ws/schemas", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlArrayAttribute("records", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
[return: System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public record[] getRecords([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] string username, [System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] [System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="integer", IsNullable=false)] string[] ids) {
object[] results = this.Invoke("getRecords", new object[] {
username,
ids});
return ((record[])(results[0]));
}
/// <remarks/>
public void getRecordsAsync(string username, string[] ids) {
this.getRecordsAsync(username, ids, null);
}
/// <remarks/>
public void getRecordsAsync(string username, string[] ids, object userState) {
if ((this.getRecordsOperationCompleted == null)) {
this.getRecordsOperationCompleted = new System.Threading.SendOrPostCallback(this.OngetRecordsOperationCompleted);
}
this.InvokeAsync("getRecords", new object[] {
username,
ids}, this.getRecordsOperationCompleted, userState);
}
private void OngetRecordsOperationCompleted(object arg) {
if ((this.getRecordsCompleted != null)) {
System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
this.getRecordsCompleted(this, new getRecordsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
}
}
还有这个:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class getRecordsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {
private object[] results;
internal getRecordsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
base(exception, cancelled, userState) {
this.results = results;
}
/// <remarks/>
public record[] Result {
get {
this.RaiseExceptionIfNecessary();
return ((record[])(this.results[0]));
}
}
}
:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]
public delegate void getRecordsCompletedEventHandler(object sender, getRecordsCompletedEventArgs e);
我之所以选择这个例子,是因为同步调用有返回类型,而异步调用没有——至少在函数调用本身中没有。我知道getRecordsCompletedEventArgs类具有正确的返回类型,这就是我将如何从调用中获取数据。我不明白的是怎么做。
假设我用getRecordsAsync:
替换当前对getRecords的调用我如何设置客户端响应时,异步调用完成?我需要使用我已经编写的LINQ过程将XML放入一个文件中,我需要记录操作的成功或失败,并且我需要通知用户操作完成。
我如何确保使呼叫实际上异步发生?我记得有一次读到,简单地调用异步SOAP方法实际上不会对当前线程异步发生,除非您先做其他事情。任何建议吗?
还有其他重要的考虑因素吗?(例如:"如果你忘记做这个,它会炸毁你的程序!")
这些都是到目前为止我还没能找到令人信服的确切答案的问题。
-
您需要处理为您自动生成的代理上的getRecordsCompleted事件,如下所示:
private void Button_Click(object sender, EventArgs e) { var proxy = new WebServiceProxy(); // Tell the proxy object that when the web service // call completes we want it to invoke our custom // handler which will process the result for us. proxy.getRecordsCompleted += this.HandleGetRecordsCompleted; // Make the async call. The UI thread will not wait for // the web service call to complete. This method will // return almost immediately while the web service // call is happening in the background. // Think of it as "scheduling" a web service // call as opposed to actually waiting for it // to finish before this method can progress. proxy.getRecordsAsync("USERNAME", new[] { 1, 2, 3, 4 }); this.Button.Enabled = false; } /// <summary> /// Handler for when the web service call returns. /// </summary> private void HandleGetRecordsCompleted(object sender, getRecordsCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.ToString()); } else { record[] result = e.Result; // Run your LINQ code on the result here. } this.Button.Enabled = true; }
-
如果您在代理上使用以Async结束的自动生成方法,则调用将异步进行-仅此而已。在我看来,您需要证明的是调用是非阻塞(也就是说,UI线程不必等待它完成),这有点棘手,因为您无法真正将自定义逻辑注入自动生成的代码中。从UI线程发出的同步调用将阻塞UI,您的应用程序将变得无响应。如果这没有发生,你的UI仍然响应按钮点击,键盘事件等,而web服务正在运行,你可以确定调用是非阻塞的。显然,如果你的web服务调用返回很快,这将很难证明。
-
你没有显示任何客户端代码,所以很难说你是否错过了什么。
For point 1
我认为你错过了一些你正在显示的代码。也许是getRecordsCompleted
的定义?我想它可能是event
类型,因此您可以将getRecordsCompletedEventHandler
类型的处理程序附加到event
,以便您可以使用异步调用的结果做一些事情。
假设您的客户端代理类名称为RecordCleint
RecordClient client = new RecordClient();
//attaching an event handler
client.getRecordsCompleted += onGetRecordsCompleted;
//calling the web service asynchronously
client.getRecordsAsync("username", [ids]);
//definition of onGetRecordsCompleted of type getRecordsCompletedEventHandler
private void onGetRecordsCompleted(object sender, getRecordsCompletedEventArgs e)
{
if(e.Error != null)
{
record[] data = e.Result;
//do something with your data
}
else
{
//handle error
}
}
[编辑]
For point 2
如果你正在用svcutil (Visual Studio> add Service reference)生成你的客户端代理,你可以信任它:)或者你可以用Visual Studio Thread窗口查看所涉及的线程。
For point 3
你可能有一些线程同步问题,例如,如果你更新一些UI组件在另一个线程,而不是UI线程所属的UI线程。所以你可能需要做一些额外的工作(dispatch)。
- Windows窗体(BeginInvoke或Dispatcher)
- WPF(调度)