The
WebService
class pre-dates the Task Parallel Library, and knows nothing about
async
tasks.
However, it
does support the older
IAsyncResult
/ APM pattern:
How to: Create Asynchronous Web Service Methods[
^]
And it's possible to implement that pattern using a
Task
:
Implementing the APM Pattern By Using Tasks[
^]
Using Tasks to implement the APM Pattern | Parallel Programming with .NET[
^]
Start with the
ToApm
extension method from Stephen Toub's blog post:
public static class TaskExtensions
{
public static Task<TResult> ToApm<TResult>(this Task<TResult> task, AsyncCallback callback, object state)
{
if (task == null) throw new ArgumentNullException(nameof(task));
if (task.AsyncState == state)
{
if (callback != null)
{
task.ContinueWith(t => callback(t), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
return task;
}
var tcs = new TaskCompletionSource<TResult>(state);
task.ContinueWith(delegate
{
if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions);
else if (task.IsCanceled) tcs.TrySetCanceled();
else tcs.TrySetResult(task.Result);
if (callback != null) callback(tcs.Task);
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
return tcs.Task;
}
}
Then, move your implementation to a private
async
method returning a
Task<string>
, and use the extension method to adapt it to the APM pattern:
public class Service1 : System.Web.Services.WebService
{
private async Task<string> PostXMLDataAsync(string session, string token)
{
...
}
[WebMethod]
public IAsyncResult BeginPostXMLDataAsync(string session, string token, AsyncCallback callback, object asyncState)
{
return PostXMLDataAsync(session, token).ToApm(callback, asyncState);
}
[WebMethod]
public string EndPostXMLDataAsync(IAsyncResult asyncResult)
{
return ((Task<string>)asyncResult).GetAwaiter().GetResult();
}
}
For the implementation, you might want to look at using the classes from the
System.Net.Http namespace[
^], as that provides a nicer async-aware API than the raw
HttpWebRequest
/
HttpWebResponse
classes.
However, if you do that, you should make sure you use a single static
HttpClient
instance for all requests, as described here:
You're using HttpClient wrong and it is destabilizing your software | ASP.NET Monsters[
^]
private static readonly HttpClient Http = new HttpClient();
private async Task<string> PostXMLDataAsync(string session, string token)
{
const string destinationUrl = "https://data.getdata.com";
string requestXml= "<ENVELOPE> <HEADER> <VERSION>1</VERSION> <REQVERSION>1</REQVERSION> <TALLYREQUEST>EXPORT</TALLYREQUEST> <TYPE>DATA</TYPE> <ID>TPGETCOMPANIES</ID><SESSIONID>" + Session + "</SESSIONID> <TOKEN>" + Token + "</TOKEN> </HEADER><BODY> <DESC> <STATICVARIABLES><SVINCLUDE>CONNECTED</SVINCLUDE></STATICVARIABLES></DESC></BODY> </ENVELOPE>";
var requestMessage = new HttpRequestMessage(HttpMethod.Post, destinationUrl);
requestMessage.Content = new StringContent(requestXml, System.Text.Encoding.UTF8, System.Net.Mime.MediaTypeNames.Text.Xml);
requestMessage.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("identity"));
requestMessage.Headers.Add("ID", "TPGETCOMPANIES");
requestMessage.Headers.Add("SOURCE", "EA");
requestMessage.Headers.Add("TARGET", "TNS");
var response = await Http.SendAsync(requestMessage).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) return "Problem in getting resp";
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}