2013-05-31 39 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