Search code examples
c#soapwindows-phone-8wsdl

How can I use async/await to call a webservice?


I have a webservice written in Yii (php framework).

I use C# and Visual Studio 2012 to develop a WP8 application. I added a service reference to my project (Add Service Reference). So I am able to use webservice functions.

   client = new YChatWebService.WebServiceControllerPortTypeClient();

   client.loginCompleted += client_loginCompleted;   // this.token = e.Result;
   client.loginAsync(this.username, this.password); 

   client.getTestCompleted += client_getTestCompleted;
   client.getTestAsync(this.token); 

function getTestAsync and loginAsync return void and both are asynchronous. Is it possible for the functions to return Task<T>? I would like to use async/await keywords in my program.


Solution

  • Assuming that loginAsync returns void, and loginCmpleted event fires when login is done, this is called the Event-based Asynchronous Pattern, or EAP.

    To convert EAP to await/async, consult Tasks and the Event-based Asynchronous Pattern. In particular, you'll want to make use of the TaskCompletionSource to convert the event-based model to a Task-based model. Once you've got a Task-based model, you can use C# 5's sexy await feature.

    Here's an example:

    // Use LoginCompletedEventArgs, or whatever type you need out of the .loginCompleted event
    // This is an extension method, and needs to be placed in a static class.
    public static Task<LoginCompletedEventArgs> LoginAsyncTask(this YChatWebService.WebServiceControllerPortTypeClient client, string userName, string password) 
    { 
        var tcs = CreateSource<LoginCompletedEventArgs>(null); 
        client.loginCompleted += (sender, e) => TransferCompletion(tcs, e, () => e, null); 
        client.loginAsync(userName, password);
        return tcs.Task; 
    }
    
    private static TaskCompletionSource<T> CreateSource<T>(object state) 
    { 
        return new TaskCompletionSource<T>( 
            state, TaskCreationOptions.None); 
    }
    
    private static void TransferCompletion<T>( 
        TaskCompletionSource<T> tcs, AsyncCompletedEventArgs e, 
        Func<T> getResult, Action unregisterHandler) 
    { 
        if (e.UserState == tcs) 
        { 
            if (e.Cancelled) tcs.TrySetCanceled(); 
            else if (e.Error != null) tcs.TrySetException(e.Error); 
            else tcs.TrySetResult(getResult()); 
            if (unregisterHandler != null) unregisterHandler();
        } 
    }
    

    Now that you've converted the Event-based async programming model to a Task-based one, you can now use await:

    var client = new YChatWebService.WebServiceControllerPortTypeClient();
    var login = await client.LoginAsyncTask("myUserName", "myPassword");