【问题标题】:Async/await call to asmx service对 asmx 服务的异步/等待调用
【发布时间】:2016-09-04 11:15:40
【问题描述】:

我正在使用 asmx 服务并在尝试调用它时尝试执行异步/等待

目前我的控制器如下所示

Public ActionResult Index()
{
   var data = _repository.GetDataFromAsmxService(somedata);
}

//RepositoryClass方法

public List<ObjectToReturn> GetDataFromAsmxService(somedata)
{
   var res = _asmxService.GetReportData(somedata);
   var result = process(res);
   return result;
}

进一步查看生成的 refrence.cs 文件后发现,等效调用存在 void async 和 onCompleted 方法。

所以我改成

        private void GetReportDataCallBack(object sender, GetReportDataCompletedEventArgs e)
        {
            var res = e.Result;
            var result = process(res);
        }

        public List<ObjectToReturn> GetDataFromAsmxService(somedata)
{
   _asmxService.GetReportDataCompleted +=GetReportDataCallBack;
   _asmxService.GetReportDataAsync(somedata);

}

但是现在回调发生在事件委托上,所以我的控制器期望返回数据不会得到它。 从控制器调用 async/await 并从 asmx webservice 获取数据的更好方法是什么

来自Refenrece.cs的代码 //生成的代码

[System.Web.Services.Protocols.SoapHeaderAttribute("ApiAuthenticationValue")]
        [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://webservice.com/GetReportData", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
        [return: System.Xml.Serialization.XmlElementAttribute(Namespace="http://webservice.com/")]
        public GetReportDataResponse GetReportData([System.Xml.Serialization.XmlElementAttribute(Namespace="http://webservice.com/")] GetReportDataRequest GetReportDataRequest) {
            object[] results = this.Invoke("GetReportData", new object[] {
                        GetReportDataRequest});
            return ((GetReportDataResponse)(results[0]));
        }

        /// <remarks/>
        public void GetReportDataAsync(GetReportDataRequest GetReportDataRequest) {
            this.GetReportDataAsync(GetReportDataRequest, null);
        }

        /// <remarks/>
        public void GetReportDataAsync(GetReportDataRequest GetReportDataRequest, object userState) {
            if ((this.GetReportDataOperationCompleted == null)) {
                this.GetReportDataOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetReportDataOperationCompleted);
            }
            this.InvokeAsync("GetReportData", new object[] {
                        GetReportDataRequest}, this.GetReportDataOperationCompleted, userState);
        }

        private void OnGetReportDataOperationCompleted(object arg) {
            if ((this.GetReportDataCompleted != null)) {
                System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
                this.GetReportDataCompleted(this, new GetReportDataCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
            }
        }

【问题讨论】:

  • 让我们看看您的存储库的完整方法签名。在您以不同的方式使用它之前,它确实依赖于它。
  • @Cameron public List GetDataFromAsmxService(somedata)
  • 你能提供GetReportData的代码吗?
  • 这是一个服务网络方法,我有来自 refrence.cs 的代码
  • @ScottChamberlain 添加了该方法的reference.cs 文件引用

标签: c# web-services asynchronous async-await asmx


【解决方案1】:

您拥有的是Event-Based Asynchronous Pattern (EAP),而您想做的是wrap that up in a task,因此您可以等待它。您可以通过TaskCompletionSource 进行此操作。

public async Task<List<ObjectToReturn>> GetDataFromAsmxServiceAsync(GetReportDataRequest somedata)
{
    var tcs = new TaskCompletionSource<GetReportDataResponse>();
    _asmxService.GetReportDataCompleted += GetReportDataCallBack;
    _asmxService.GetReportDataAsync(somedata, tcs); //we pass tcs in so it can be used from the callback.
    GetReportDataResponse res;
    try
    {
        res = await tcs.Task;
    }
    finally
    {
        //unsubscribe from the handler when done so we don't get a leak.
        _asmxService.GetReportDataCompleted -= GetReportDataCallBack;
    }

    var result = process(res);
    return result;
}

private void GetReportDataCallBack(object sender, GetReportDataCompletedEventArgs e)
{
    var tcs = (TaskCompletionSource<GetReportDataResponse>)e.UserState;
    if (e.Cancelled)
    {
        tcs.TrySetCanceled();
    }
    else if (e.Error != null)
    {
        tcs.TrySetException(e.Error);
    }
    else
    {
        tcs.TrySetResult(e.Result);
    }
}

您还需要将控制器更改为异步

public Task<ActionResult> IndexAsync()
{
    var data = await _repository.GetDataFromAsmxServiceAsync(somedata);
}

Here is a good article 解释了如何从同步 MVC 代码转换为异步 MVC 代码,并解释了为什么即使 Index 现在被称为 IndexAsync 它仍然可以作为您的 Index 控制器。

【讨论】:

  • 如果在存储库中抛出异常,异常会以这种模式传播到控制器
  • 是的,它确实应该像非异步版本一样处理异常。他们将在等待线上被提升。
  • 传播异常的东西是tcs.TrySetException(e.Error);,当你awaitGetDataFromAsmxServiceAsync和@中返回任务时,它会导致从事件内部异步引发的任何异常987654336@
  • @JMD 另外,Task&lt;GetReportDataResponse&gt; res = _asmxService.GetReportDataAsync(somedata); 不会编译,GetReportDataAsync 返回void 而不是Task&lt;GetReportDataResponse&gt;。如果它确实返回Task&lt;GetReportDataResponse&gt;,则整个代码可以简化为result = process(await _asmxService.GetReportDataAsync(somedata));TaskCompletionSource 的全部要点是将使用事件显示您何时完成的东西转换为使用任务显示何时完成的东西。阅读我关于 EAP 的回答中的第一个链接以获取更多信息。
  • @JMD 任务版本的实际术语是“Task-based Asynchronous Pattern (TAP)”,还有第三种“Asynchronous Programming Model (APM)”。 APM 是您在函数中看到的BeginXxxxx(/EndXxxxxx(IAsyncResult result) 对。要从 APM->TAP 使用 TaskFactory.FromAsync(,对于 EAP->TAP,您使用 TaskCompletionSource,如我的答案所示。
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-06
  • 2021-06-20
  • 2021-06-30
相关资源
最近更新 更多