0

I am trying to call a WebService inside my IHttpAsyncHandler and I see there is an answer like this Using an IHttpAsyncHandler to call a WebService Asynchronously

I have questions about the answer. I appreciate if someone can help me.

It has

Task webClientDownloadTask = webClientDownloadCompletionSource.Task;

My questions are

  1. webClientDownloadCompletionSource is not related to Webclient (client) object, so what is the point of doing this:

    // Get the TCS's task so that we can append some continuations Task webClientDownloadTask = webClientDownloadCompletionSource.Task;

  2. What is "taskCompletionSource" in here:

        // Signal the TCS that were done (we don't actually look at the bool result, but it's needed)
        taskCompletionSource.SetResult(true);
    
  3. Why I dispose() the WebClient in the ContinueWith() callback, why not just dispose() WEbClient after we set the taskCompletionSource.SetResult(true);?

    // Always dispose of the client once the work is completed webClientDownloadTask.ContinueWith( _ => { client.Dispose(); }, TaskContinuationOptions.ExecuteSynchronously);

Here is the full code:

    public class MyAsyncHandler : IHttpAsyncHandler
{
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        // NOTE: the result of this operation is void, but TCS requires some data type so we just use bool
        TaskCompletionSource<bool> webClientDownloadCompletionSource = new TaskCompletionSource<bool>();

        WebClient webClient = new WebClient())
        HttpContext currentHttpContext = HttpContext.Current;

        // Setup the download completed event handler
        client.DownloadDataCompleted += (o, e) =>
        {
            if(e.Cancelled)
            {
                // If it was canceled, signal the TCS is cacnceled
                // NOTE: probably don't need this since you have nothing canceling the operation anyway
                webClientDownloadCompletionSource.SetCanceled();
            }
            else if(e.Error != null)
            {
                // If there was an exception, signal the TCS with the exception
                webClientDownloadCompletionSource.SetException(e.Error);
            }
            else
            {
                // Success, write the response
                currentHttpContext.Response.ContentType = "text/xml";
                currentHttpContext.Response.OutputStream.Write(e.Result, 0, e.Result.Length);

                // Signal the TCS that were done (we don't actually look at the bool result, but it's needed)
                taskCompletionSource.SetResult(true);
            }
        };

        string url = "url_web_service_url";

        // Kick off the download immediately
        client.DownloadDataAsync(new Uri(url));

        // Get the TCS's task so that we can append some continuations
        Task webClientDownloadTask = webClientDownloadCompletionSource.Task;

        // Always dispose of the client once the work is completed
        webClientDownloadTask.ContinueWith(
            _ =>
            {
                client.Dispose();
            },
            TaskContinuationOptions.ExecuteSynchronously);

        // If there was a callback passed in, we need to invoke it after the download work has completed
        if(cb != null)
        {
            webClientDownloadTask.ContinueWith(
               webClientDownloadAntecedent =>
               {
                   cb(webClientDownloadAntecedent);
               },
               TaskContinuationOptions.ExecuteSynchronously);
         }

        // Return the TCS's Task as the IAsyncResult
        return webClientDownloadTask;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        // Unwrap the task and wait on it which will propagate any exceptions that might have occurred
        ((Task)result).Wait();
    }

    public bool IsReusable
    {
        get 
        { 
            return true; // why not return true here? you have no state, it's easily reusable!
        }
    }

    public void ProcessRequest(HttpContext context)
    {
    }
}
Community
  • 1
  • 1
n179911
  • 19,547
  • 46
  • 120
  • 162

1 Answers1

0

Check this for an idea of what it's used for. There is also an example at the end

TaskCompletionSource

In many scenarios, it is useful to enable a Task to represent an external asynchronous operation. TaskCompletionSource{TResult} is provided for this purpose. It enables the creation of a task that can be handed out to consumers, and those consumers can use the members of the task as they would any other. However, unlike most tasks, the state of a task created by a TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the completion of the external asynchronous operation to be propagated to the underlying Task. The separation also ensures that consumers are not able to transition the state without access to the corresponding TaskCompletionSource.

Nkosi
  • 235,767
  • 35
  • 427
  • 472