Async & Threading

Dec 26, 2013 at 1:18 PM
Just to preface, I am fairly new to WPF and threading... What is the best method when calling a long running ServiceAgent? I'm basically getting data from our system through an existing API. The call on the API can take up to 3 minutes depending on the data requested. I want my users to be able to continue working while the data is being retrieved.

Currently I am using the following:
        Task T = new Task(() =>
            {
                base.Model = serviceAgent.GetJob(JobNumber, JobASM);
            });
        T.Start();
Is this the best method for background threads from a VM or is there a better approach?

Thank you,

Brent
Dec 26, 2013 at 2:43 PM
Edited Dec 26, 2013 at 2:45 PM
Here's what you need to do. First, define a method in the service agent interface that returns a Task<T>:
public interface ICustomerServiceAgent
{
    Task<Customer> GetCustomerAsync(int id);
}
This makes the method awaitable, so that you can use the async / await pattern for performing long-running tasks asynchronously. The nice thing is that this pattern automatically puts you back on the UI thread, so that you can update view-model properties that are bound to UI elements. If your long-running task is IO bound, such as a web service or database call, then it probably exposes an awaitable method that you can call from within your service agent. This code sample awaits a Task.Delay to simulate the IO operation. If your task is compute-bound, you can run it on a thread pool thread by calling Start on a new Task, as in your snippet.
public async Task<Customer> GetCustomerAsync(int id)
{
    // Simulate long-running task
    await Task.Delay(TimeSpan.FromSeconds(5));

    // Executed after delay
    return new Customer
    {
        CustomerId = id,
        CustomerName = "John Doe",
        City = "Dallas"
    };
}
The view-model can have a property that is used for enabling or disabling a button, or otherwise indicate in the view that the operation is taking place. Use the mvvmprop code snippet to create the property. For example, here is a boolean property that defaults to true and is bound to the button IsEnabled property.
private bool _isNotBusy = true;
public bool IsNotBusy
{
    get { return _isNotBusy; }
    set
    {
        _isNotBusy = value;
        NotifyPropertyChanged(m => m.IsNotBusy);
    }
}
Finally, you need a method in the view-model that awaits the service agent async method.
public async void NewCustomer()
{
    IsNotBusy = false;
    base.Model = await serviceAgent.GetCustomerAsync(1);
    IsNotBusy = true;
}
Here is the xaml section of the button that shows the binding of IsEnabled to IsNotBusy.
<Button Content="New Customer" Grid.Row="3" Grid.ColumnSpan="2" 
        Height="30" Width="100" IsEnabled="{Binding IsNotBusy}">
Let me know if you'd like to get the source code for the project. I hope this helps.

Cheers,
Tony
Marked as answer by bpryer on 12/26/2013 at 7:53 AM
Dec 26, 2013 at 3:53 PM
Tony,

I can't thank you enough! That was a very thorough explanation. I'm really enjoying your toolkit... You have done an outstanding job documenting and getting users up to speed quickly. What are the methods of giving back? Some of us might not be able to give time assisting with toolkit development, but we can help out financially...

thanks,

Brent
Dec 28, 2013 at 11:47 AM
You've inspired me! I just put a link to a donations page on the project home page: https://simplemvvmtoolkit.codeplex.com. Any contribution would be appreciated and enable me to devote more time to keeping the toolkit current, as well as add more examples, tutorials and screencasts.

Cheers!
Tony