2013-05-31 37 views
2

所以我正在研究一个使用Web服务的客户端。我使用服务中的WSDL和XSD文件生成代理类,并且所有同步函数都可以正常工作。但是,考虑到它们的同步性质,进行任何呼叫都会导致UI在呼叫结束之前停止响应。使用异步方法的经典原因,对吗?在C#4.0 Web服务客户端中需要帮助实现异步调用

问题是,我还在读学位,对于异步编程知之甚少。我试图在网上读到它(我的雇主甚至有一本书全天候订阅),但我很难理解我应该如何进行呼叫以及如何处理响应。下面是我有:

/// <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类具有正确的返回类型,这就是我将如何从调用中获取数据的方式。我似乎无法弄清楚的是如何真正做到这一点。

比方说,我代替我的当前呼叫getRecords与getRecordsAsync:

  1. 如何建立客户端异步调用完成时如何应对?我需要使用已写入的LINQ过程将XML放入文件中,我需要记录操作的成功或失败,并且需要通知用户操作已完成。

  2. 我该如何确保使呼叫实际发生异步?我记得在某个时候读到,只是调用一个异步SOAP方法实际上并不是针对当前线程异步发生的,除非您先做其他事情。有小费吗?

  3. 我还有什么其他的主要考虑因素吗?(如:“如果你忘记这样做,它会炸掉你的程序!”)

这些都是我一直没能找到令人信服的答案,公司到目前为止的所有问题。提前感谢您提供任何帮助。

+0

我不是100%在这个,但你想尝试在Web服务内异步调用的东西?如果Web服务需要很长时间才能完成,那么应该是异步的代码应该是客户端代码,而不是服务代码。这个想法是,如果你在服务中产生一个新的线程,现有的线程将退出并返回SOAP响应,并且你的客户端将永远不会收到发生在服务端的事件处理程序的通知。 – Richthofen

回答

4
  1. 您需要处理上这是自动生成的你,像这样的代理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; 
    } 
    
  2. 如果您使用au在以Async结尾的代理上生成方法,该调用将异步进行 - 就是这样。对我来说,你需要证明的是,该电话是非阻塞(也就是说,UI线程不必等待它完成),这是有点棘手,因为你不能真正注入自定义逻辑放入自动生成的代码中。 从UI线程进行的同步调用将阻止用户界面,并且您的应用程序将无响应。如果这种情况没有发生,并且您的UI在网络服务正在运行时仍然响应按钮点击,键盘事件等,则可以确定该呼叫是非阻塞的。很明显,要证明您的Web服务调用是否快速返回,将会非常棘手。

  3. 你没有显示任何客户端代码,所以很难说如果你错过了任何东西。

+0

这工作。现在我比以前更了解异步。谢谢!! – Ant

1

对于点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 
    } 
} 

[编辑]

对于点2

如果生成你的客户端代理与svcutil(Visual Studio>添加服务引用),你可以信任它:)或者你可以用Visual Studio 线程窗口观看涉及的线程。

对于点3

,如果你属于他们的地方,以更新另一个线程比UI线程一些UI组件,您可能有一些线程同步问题,例如。所以你可能需要做一些额外的工作(调度)。

+0

感谢您的回复。关于调度的额外信息为我编写了以前没有的用户界面编程提供了一些见解。 – Ant