Da async/await pattern blev introduceret med C# 5, blev hele .NET frameworket gennemarbejdet, så relevante asynkrone metoder blev awaitable og fik Task som returværdi.

Det gælder ikke Windows Phone API’en.  Specielt gælder det ikke for WebClient, der i .NET 4.5 bl.a. har fået DownloadDataTaskAsync, der returnerer en Task, og dermed er awaitable.  Til Silverlight må man nøjes med DownloadDataAsync, som er baseret på Event-Based Asynchronous Pattern (EAP) gennem DownloadDataCompleted.

I System.Threading.Tasks namespacet findes en klasse kaldet TaskCompletionSource.  Denne klasse er netop beregnet til EAP scenarier, fordi den opretter en task, som man kan sætte op til at vente på, at en event bliver kaldt.  Dette gøres vha. af SetResult og SetException.  Kalder man en af de to metoder, vil den tilhørende Task afsluttes.  Vha. af TaskCompletionSource har jeg lavet følgende awaitable extension metoder til WebClient, som gør det muligt at lave awaitables for hver af de 4 HTTP metoder GET, POST, PUT og DELETE.  Metoderne benytter JSON via Json.NET:

    public static class WebClientExtensions
    {
        public static Task<T> GetAsync<T>(this WebClient client, Uri uri)
        {
            var tcs = new TaskCompletionSource<T>();
            client.DownloadStringCompleted += (s, e) =>
                {
                    if (e.Error == null)
                    {
                        T result = JsonConvert.DeserializeObject<T>(e.Result);
                        tcs.SetResult(result);
                    }
                    else
                    {
                        tcs.SetException(e.Error);
                    }
                };
            client.DownloadStringAsync(uri);
            return tcs.Task;
        }

        public static Task PostAsync<T>(this WebClient client, Uri uri, T item)
        {
            var tcs = new TaskCompletionSource<string>();
            client.Headers["Content-Type"] = "application/json";
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            string data = JsonConvert.SerializeObject(item);

            client.UploadStringAsync(uri, "POST", data);
            return tcs.Task;
        }

        public static Task PutAsync<T>(this WebClient client, Uri uri, T item)
        {
            var tcs = new TaskCompletionSource<string>();
            client.Headers["Content-Type"] = "application/json";
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            string data = JsonConvert.SerializeObject(item);

            client.UploadStringAsync(uri, "PUT", data);
            return tcs.Task;
        }

        public static Task DeleteAsync(this WebClient client, Uri uri)
        {
            var tcs = new TaskCompletionSource<string>();
            client.UploadStringCompleted += (s, e) =>
            {
                if (e.Error == null)
                {
                    tcs.SetResult(e.Result);
                }
                else
                {
                    tcs.SetException(e.Error);
                }
            };

            client.UploadStringAsync(uri, "DELETE", "");
            return tcs.Task;
        }

Du kan læse mere om TaskCompletionSource hos Stephen Toub, som er en af de ypperste, når det kommer til parallelisering i .NET.

Jeg har haft stor glæde af de ovenstående WebClient extensions, men min glæde var endnu større, da jeg fornylig læste, at Microsoft arbejder på en port af HttpClient til Windows Phone, og har gjort det tilgængeligt via NuGet.  Jeg har endnu ikke prøvet det, men det virker lovende.

Kommentarerne er lukkede