3
votes

I am consuming a asmx service and trying to do async/await while trying to call it

Currently My controller is like below

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

//RepositoryClass Method

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

Upon further looking at refrence.cs file generated found there were void async and onCompleted methods for equivalent calls.

So i changed it to

        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);

}

but now the callback happens to the Event delegate so my controller expecting a return data won’t get it. what is a better way to call async/await from controller and get the data from asmx webservice

Code From Refenrece.cs //Generated code

[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));
            }
        }
1
Let's see the full method signatures of your repository. It really relies on what that is before you can consume it a different way.Cameron
@Cameron public List<ObjectToReturn> GetDataFromAsmxService(somedata)Justin Homes
Can you provide the code for GetReportData?Scott Chamberlain
that is a service webmethod, i have code from refrence.csJustin Homes
@ScottChamberlain added the reference.cs file ref of that methodJustin Homes

1 Answers

7
votes

What you have is a Event-Based Asynchronous Pattern (EAP) and what you want to do is wrap that up in a task so you can await it. You do this via a 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);
    }
}

You will also need to change your controller to be async too

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

Here is a good article explaining how to convert from synchronous MVC code to async MVC code and explains why even though Index is now called IndexAsync it will still work as your controller for Index.