2016-05-17 115 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”由斯蒂芬·克利里的一些其他的选择。