2016-05-17 137 views
2

我們有一個傳統的VB6應用程序,它使用用C#(.NET 4.5)編寫的ASMX webservice,後者又使用庫(C#/ .NET 4.5)執行某些業務邏輯。其中一個庫方法會觸發長時間運行的數據庫存儲過程,最後我們需要啓動另一個使用存儲過程生成的數據的進程。因爲其中一個要求是控件必須在調用webservice後立即返回到VB6客戶端,庫方法是async,將Action回調作爲參數,webservice將回調定義爲匿名方法,而不是await結果庫方法調用。帶回調的.NET異步webservice調用

在高層次上,它看起來像這樣:

 
using System; 
using System.Data.SqlClient; 
using System.Threading.Tasks; 
using System.Web.Services; 

namespace Sample 
{ 
    [WebService(Namespace = "urn:Services")] 
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
    public class MyWebService 
    { 
     [WebMethod] 
     public string Request(string request) 
     { 
      // Step 1: Call the library method to generate data 
      var lib = new MyLibrary(); 
      lib.GenerateDataAsync(() => 
      { 
       // Step 2: Kick off a process that consumes the data created in Step 1 
      }); 

      return "some kind of response"; 
     } 
    } 

    public class MyLibrary 
    { 
     public async Task GenerateDataAsync(Action onDoneCallback) 
     { 
      try 
      { 
       using (var cmd = new SqlCommand("MyStoredProc", new SqlConnection("my DB connection string"))) 
       { 
        cmd.CommandType = System.Data.CommandType.StoredProcedure; 
        cmd.CommandTimeout = 0; 
        cmd.Connection.Open(); 

        // Asynchronously call the stored procedure. 
        await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 

        // Invoke the callback if it's provided. 
        if (onDoneCallback != null) 
         onDoneCallback.Invoke(); 
       } 
      } 
      catch (Exception ex) 
      { 
       // Handle errors... 
      } 
     } 
    } 
} 

在本地測試上述工作,但是當代碼被部署爲web服務步驟2從不執行,即使步驟1存儲過程完成並生成數據。

任何想法我們做錯了什麼?

+0

您的本地機器的防火牆可能會阻止傳入的連接... – Eser

+0

另外,我想第1步調用'lib.GenerateDataAsync'(說'GenerateData',它沒有顯示)。我認爲問題在於asmx請求已經完成執行並且「正在出門」,並且「無處執行」了回調。你有沒有試過等待那個電話? –

+0

查看您的請求實施。如果您使用異步,則必須在整個管道中使用它以獲得最一致的解決方案。 –

回答

1

我發現我的問題的解決方案,涉及到舊式(開始/結束)方法的代碼異步執行:

public void GenerateData(Action onDoneCallback) 
    { 
     try 
     { 
      var cmd = new SqlCommand("MyStoredProc", new SqlConnection("my DB connection string")); 
      cmd.CommandType = System.Data.CommandType.StoredProcedure; 
      cmd.CommandTimeout = 0; 
      cmd.Connection.Open(); 

      cmd.BeginExecuteNonQuery(
       (IAsyncResult result) => 
       { 
        cmd.EndExecuteNonQuery(result); 
        cmd.Dispose(); 

        // Invoke the callback if it's provided, ignoring any errors it may throw. 
        var callback = result.AsyncState as Action; 
        if (callback != null) 
         callback.Invoke(); 
       }, 
       onUpdateCompleted); 
     } 
     catch (Exception ex) 
     { 
      // Handle errors... 
     } 
    } 

onUpdateCompleted回調動作傳遞給BeginExecuteNonQuery方法作爲第二參數,然後在AsyncCallback(第一個參數)中消耗。這在VS內部調試和部署到IIS時都很有用。

2

將任務放在IIS上運行是很危險的,應用程序域可能會在方法完成之前關閉,這可能是發生在您身上的事情。如果您使用HostingEnvironment.QueueBackgroundWorkItem,則可以告訴IIS有需要繼續運行的工作。這將使應用程序域活着一個額外的90秒(默認)

using System; 
using System.Data.SqlClient; 
using System.Threading.Tasks; 
using System.Web.Services; 

namespace Sample 
{ 
    [WebService(Namespace = "urn:Services")] 
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
    public class MyWebService 
    { 
     [WebMethod] 
     public string Request(string request) 
     { 
      // Step 1: Call the library method to generate data 
      var lib = new MyLibrary(); 
      HostingEnvironment.QueueBackgroundWorkItem((token) => 
       lib.GenerateDataAsync(() => 
       { 
        // Step 2: Kick off a process that consumes the data created in Step 1 
       })); 

      return "some kind of response"; 
     } 
    } 

    public class MyLibrary 
    { 
     public async Task GenerateDataAsync(Action onDoneCallback) 
     { 
      try 
      { 
       using (var cmd = new SqlCommand("MyStoredProc", new SqlConnection("my DB connection string"))) 
       { 
        cmd.CommandType = System.Data.CommandType.StoredProcedure; 
        cmd.CommandTimeout = 0; 
        cmd.Connection.Open(); 

        // Asynchronously call the stored procedure. 
        await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 

        // Invoke the callback if it's provided. 
        if (onDoneCallback != null) 
         onDoneCallback(); 
       } 
      } 
      catch (Exception ex) 
      { 
       // Handle errors... 
      } 
     } 
    } 
} 

如果你想要的東西超過90秒外更可靠參見文章「Fire and Forget on ASP.NET」由斯蒂芬·克利裏的一些其他的選擇。