Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/31.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 带有回调的.NET异步webservice调用_C#_Asp.net_Web Services_Asynchronous_Async Await - Fatal编程技术网

C# 带有回调的.NET异步webservice调用

C# 带有回调的.NET异步webservice调用,c#,asp.net,web-services,asynchronous,async-await,C#,Asp.net,Web Services,Asynchronous,Async Await,我们有一个遗留的VB6应用程序,它使用一个用C#(.NET4.5)编写的ASMXWebService,它反过来使用一个库(C#/.NET4.5)来执行一些业务逻辑。其中一个库方法触发一个长时间运行的数据库存储过程,最后我们需要启动另一个进程,该进程使用存储过程生成的数据。因为其中一个要求是控件必须在调用Web服务后立即返回VB6客户端,所以库方法是async,将操作回调作为参数,Web服务将回调定义为匿名方法,而不等待库方法调用的结果 从高层次来看,它是这样的: using System; us

我们有一个遗留的VB6应用程序,它使用一个用C#(.NET4.5)编写的ASMXWebService,它反过来使用一个库(C#/.NET4.5)来执行一些业务逻辑。其中一个库方法触发一个长时间运行的数据库存储过程,最后我们需要启动另一个进程,该进程使用存储过程生成的数据。因为其中一个要求是控件必须在调用Web服务后立即返回VB6客户端,所以库方法是
async
,将
操作
回调作为参数,Web服务将回调定义为匿名方法,而不
等待
库方法调用的结果

从高层次来看,它是这样的:

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... } } } } 使用制度; 使用System.Data.SqlClient; 使用System.Threading.Tasks; 使用System.Web.Services; 名称空间示例 { [WebService(Namespace=“urn:Services”)] [WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)] 公共类MyWebService { [网络方法] 公共字符串请求(字符串请求) { //步骤1:调用library方法生成数据 var lib=新的MyLibrary(); lib.GenerateDataAsync(()=> { //步骤2:启动使用步骤1中创建的数据的流程 }); 返回“某种响应”; } } 公共类图书馆 { 公共异步任务GenerateDataAsync(操作onDoneCallback) { 尝试 { 使用(var cmd=new-SqlCommand(“MyStoredProc”),new-SqlConnection(“mydb连接字符串”)) { cmd.CommandType=System.Data.CommandType.StoredProcess; cmd.CommandTimeout=0; cmd.Connection.Open(); //异步调用存储过程。 wait cmd.ExecuteNonQueryAsync().ConfigureWait(false); //如果提供了回调,则调用该回调。 if(onDoneCallback!=null) onDoneCallback.Invoke(); } } 捕获(例外情况除外) { //处理错误。。。 } } } } 上述方法在本地测试中有效,但当代码部署为Web服务时,即使步骤1存储过程完成并生成数据,也不会执行步骤2


知道我们做错了什么吗?

我找到了一个解决问题的方法,它涉及到异步执行代码的旧式(开始/结束)方法:

    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
回调操作作为第二个参数传递给
BeginExecutenQuery
方法,然后在
AsyncCallback
中使用(第一个参数)。无论是在VS内部调试还是部署到IIS时,这都是一种魅力。

让任务在IIS上运行是危险的,应用程序域可能在方法完成之前关闭,这很可能是您遇到的情况。如果您使用,您可以告诉IIS有正在发生的工作需要保持运行。这将使应用程序域多活90秒(默认情况下)


如果您想要比额外90秒更可靠的东西,请参阅Stephen Cleary的文章以了解其他一些选项。

您的本地计算机的防火墙可能正在阻止传入连接…顺便说一下,我猜步骤1正在调用
lib.GenerateDataAsync
(未显示
GenerateData
)。我认为问题在于asmx请求已经执行完毕,并且“已经出门了”,并且回调“无处执行”。您是否尝试过等待该呼叫?请检查您的请求执行情况。如果您使用的是async,您必须在整个管道中使用它,以获得最一致的解决方案。@MarkLarter,感谢您注意我的输入错误,我已经修复了它。在web服务中等待调用将破坏运行存储过程的整个“触发并忘记”方法。我同意,可能的原因是,在调用完成时,运行对
GenerateDataAsync
的调用的线程被IIS回收,因此回调没有上下文可供执行。我只是希望“曾经在那里,做过那个”的人能提出一个解决办法。@Caspiananuck肯定是BTDT,但不是从.NET2.0(BeginAsync…)开始。等待仍然应该释放IIS工作线程来处理其他web请求,这样您就不会阻塞请求管道。我不确定你还需要通过“开火并忘记”来完成什么,所以不可否认,这可能不足以满足你的需要。
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...
            }
        }
    }
}