Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchrony with TAP (async/await) in WPF MVVM

Tags:

c#

mvvm

wpf

I am looking for a best practice to the following setup.

A View is data bound to ViewModel (through WPF). ViewModel updates View via INotifyPropertyChanged. Model updates ViewModel via events. The Model knows about stuff from the outside-world, like how to get stuff from the internet via WebClient, and how to retrieve data from a database.

Getting and sending stuff to the outside-world should be done asynchronously in order for the UI (and by extension, the user) not to suffer from waiting on the outside-world.

What would be the best practice way to go about this?

1. The ViewModel should be responsible for calling model methods asynchronously.
This would have the advantage of being able to write stuff like

GetWebPage(string url) {
  var result = await Model.GetWebPageAsync(url);
  Url = result.Url;
}

in the ViewModel where Url is a ViewModel property with INotifyPropertyChanged to update the View. Or even

GetWebPage(string url) {
  var result = await Model.GetWebPageAsync(url);
  _view.Url = result.Url;
}

where we can avoid the INotifyPropertyChanged all together. Which of these ways do you prefer?

However, it might be more sensible to have the Model itself do the asynchronous work. We might want to be able to use the Model without the View and ViewModel, while still having it work asynchronously. Another argument is that, who knows better that the Model what stuff is best handled asynchronously.

2. The Model handles all the asynchronous stuff by itself. The ViewModel code becomes more like

GetWebPage(string url) {
  Model.GetWebPage(url);
}

and in the Model

GetWebPage(string url) {
  var result = await Model.GetWebPageAsync(url);
  if (UrlChanged != null);
     UrlChanged(this, new UrlChangedEventArgs(url));
}

that the ViewModel can subscribe to, and update the View accordingly.

Which of the ways do you think is the best practice?

like image 421
kasperhj Avatar asked Oct 29 '12 10:10

kasperhj


1 Answers

The third way: view model makes asynchronous call, but it uses client-side service to retrieve web page, model itself is anemic (it knows nothing about outer world):

GetWebPage(string url) 
{
  var dataService = anyServiceLocator.GetService<IDataService>();
  var result = await dataService.GetWebPageAsync(url, Model);
  Url = result.Url;
}

This allows to change real data downloading algorithm, e.g. for testing purposes.

like image 88
Dennis Avatar answered Nov 12 '22 10:11

Dennis