Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async and populating data for WPF application

I have a WPF application that initializes the state of the UI via methods in the constructor. However, it's never returning from the Wait(); in the constructor.

Here's what I am currently doing via a fairly-contrived sample:

public class SomeViewModel
{
    public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
    public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}   

    public SomeViewModel() 
    {
        //Load synch. here
        LoadData().Wait();      
    }

    public async Task LoadData()
    {
        UIData = await Task.Run(() => SomeService.SelectAllUIData());
    }

    public async void AddDataExecute()
    {
        //Add new data item to database on separate thread to keep UI responsive
        await Task.Run(() => SomeService.AddNewData(0));

        //Reload data from database and update UI, has to happen after AddNewData completes
        await LoadData();
    }
}

I believe it's hanging because I am never actually returning a Task. However, I don't know of a different way to assign UIData asynchronously that works both in the constructor and the Commands that call it.

like image 513
Killnine Avatar asked Jan 29 '14 16:01

Killnine


2 Answers

Don't construct the object through a constructor, if you require construction to be asynchronous. Use a static factory method:

public class SomeViewModel
{
    private SomeViewModel() 
    { }

    public static async Task<SomeViewModel> Create()
    {
        SomeViewModel model = new SomeViewModel();
        await model.LoadData();
        return model;
    }

    public async Task LoadData()
    {
        UIData = await Task.Run(() => SomeService.SelectAllUIData());
    }

    //...
}

As for why your code isn't working, you're getting the standard await deadlock in which you're blocking on an asynchronous operation, that blocking is preventing continuations from being called on the UI thread, and with two different operations each waiting on the other to continue, you get a deadlock. This is why it's important to "async all the way up" rather than synchronously blocking on an asynchronous operation.

like image 147
Servy Avatar answered Oct 14 '22 15:10

Servy


You're seeing the classic deadlock situation that I describe on my blog.

To solve it, use async all the way, as I describe in my MSDN article on async best practices.

This can be difficult in some scenarios. I have a blog post describing a few approaches for async constructors. However, since you're talking about a ViewModel and data for the UI, you'll probably find my blog post on async properties more helpful - in particular, the section on data binding.

like image 39
Stephen Cleary Avatar answered Oct 14 '22 13:10

Stephen Cleary