When a property changes its value I want to call an async method that fetches data from a web service an then updates another property to which the UI is bound causing the UI to update. It makes sense to me that the update is async as I want the UI to remain responsive while the update is going on.
Is it wrong to call an async method from the non async setter? I noticed that if the async method return void then VS does not complain but if it returns Task then visual studio complains that the call is not awaited. My code looks like this:
public int Property1
{
set
{
_property1 = value;
NotityPropertyChanged();
UpdateData();
}
}
private async void UpdateData()
{
// show data loading message
var data = await GetDataFromWebService();
Property2 = data;
// hide data loading message
}
It seems to work but I wonder if I am not using async the way it was intended given the warning I get from VS if the return type is Task.
UPDATE: Several answers and comments have suggest the user of commands rather than updating in response to a change in a property. For my case I am not sure how that could be applied so am providing more details about how the UI is expected to work.
In the user interface there is date picker (which is bound to the property in question on the view model) where the user selects the date for which he wants to view records. When a new date is selected by the user, the app should show a busy indicator, and then fetch records in the background to avoid blocking the UI thread. Preferably I want the update to be initiated when the date selected, without requiring the user to push a button after the date is selected.
Would it be better to bind the SelectionChanged event of the date picker to and async command on the ViewModel or alternatively have an sync handler for the SelectionChanged which calls the update method on the view model directly?
Is it wrong to call an async method from the non async setter?
In short, yes. Properties should not not be kicking off asynchronous background operations in their setters.
I recommend you to read Stephen Cleary's blog post and MSDN article on the subject:
Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding: https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
Async OOP 3: Properties: https://blog.stephencleary.com/2013/01/async-oop-3-properties.html
You may also want to look into a functional MVVM framework such as ReactiveUI that handles this scenario by converting properties into observable streams of values that you can subscribe to: https://reactiveui.net/docs/getting-started/
It's not the "cleanest" way as already stated, but you can leave it. It's not harmful in the first place except you are hiding an unexpected long and expensive operation behind a quick assignment.
MSDN says:
An async method can also have a void return type. This return type is used primarily to define event handlers, where a void return type is required. Async event handlers often serve as the starting point for async programs.
An async method that has a void return type can’t be awaited, and the
caller of a void-returning method can't catch any exceptions that the
method throws.
A simple solution could be to listen to the PropertyChanged event. This would be more like a work around to escape the setter. A better way is to make your ViewModel expose an update command by implementing the ICommand interface asynchrounous by adding asynchronous execution to it. You would then invoke this AsyncCommand from your View, whenever the property changes. You could also pass the new value as a command parameter to the ViewModel.
Automatically calling updates when just a property changes is usually a bad idea of itself.
Usually a button and a command are a better plan.
You could alternatively invoke a command from the UI rather than use that setter. And that'd be relatively simple to do.
At a minimum, you want a different thread for calling some web service in that sort of a way.
A fire and forget thread wouldn't exactly be terrible.
So long as you don't care so much what happens after you forgot it.