简体   繁体   中英

Moving from Event-Based Async to Task-Based Async

I am using a WCF service to load some data in a WPF application and, until recently, did so via the Event-Based Async methods that Visual Studio auto-generated for me:

//Old way
private void LoadFoos(int barId)
{
    serviceClient.SelectFoosByBarIdCompleted += (s, e) =>
    {
        Foos = e.Result.OrderBy(f => f.Description).ToList();
    });
    serviceClient.SelectFoosByBarIdAsync();
}

For whatever reason, we moving to using Tasks and I had a question on the best way to do the same sort of thing:

//New way
private async void LoadFoos(int barId)
{
    private TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    serviceClient.SelectFoosByBarIdAsync(barId).ContinueWith(t => 
    {
        Foos = t.Result.OrderBy(f => f.Description).ToList();
    }, uiTaskScheduler);
}

I think this is uglier because I have to manually set the context so I don't update things on the wrong thread ( Foos is a data-bound property). Also, I thought I'd be able to do this:

//New way #2, doesn't sort ;(
private async void LoadFoos(int barId)
{
    private TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    var selectFoosTask = serviceClient.SelectFoosByBarIdAsync(barId);
    Foos = selectFoosTask;
}

But then I can't order it according to Description .

The whole task concept is fairly new to me so maybe I am missing something. Is there a more succinct way than what I've listed above?

You would just use await, not a continuation:

private async Task LoadFoos(int barId)
{
    var temp = await serviceClient.SelectFoosByBarIdAsync(barId);

    Foos = temp.OrderBy(f => f.Description).ToList();
}

Note that using an async void, or even just a Task returning method is probably not ideal. It would likely be better to rewrite this as (*assuming Foos is a List<string> ):

private async Task<List<string>> LoadFoosAsync(int barId)
{
    var temp = await serviceClient.SelectFoosByBarIdAsync(barId);
    return temp.OrderBy(f => f.Description).ToList();
}

Then, when you call this, use:

Foos = await LoadFoosAsync(id);

As for converting event based async methods to task based ones (Tasks are vastly superior by the way:) ) check out these blog posts

http://msdn.microsoft.com/en-us/magazine/ff959203.aspx http://blogs.msdn.com/b/pfxteam/archive/2009/06/19/9791857.aspx

You can still do all the processing before you marshal back to the ui thread. You can also create your own ContinueWith helper method that puts the task on correct TaskScheduler. (if you can't use await, that is the simplest option)

Also note that newer versions (2012 and later I think) of the wsdl tool actually generates Task based async methods for services.

Since the method is async just await the task rather than manually adding a continuation:

private async Task LoadFoos(int barId)
{
    Foos = (await serviceClient.SelectFoosByBarIdAsync(barId))
        .OrderBy(f => f.Description).ToList();
}

Also note you should avoid async void methods whenever possible, as you then have no way of knowing when the asynchronous operation ends, or to access any exceptions thrown. Instead have the method return a Task . Better yet would be to have the method return a Task<T> where T is the data that this returns, rather than having the method set some other field.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM