1

I have a working Async method (web scrape) that works, but cannot update a View. I can see the INotifyPropertyChanged event is not firing.

In my constructor I call the async method like so:

Task<ObservableCollection<Team>> TeamRows2 = Scrape();

My Scrape method returns:

ObservableCollection<Team> rows 

By this method signature:

private async Task<ObservableCollection<Team>> Scrape()
ObservableCollection<Team> rows = new ObservableCollection<Team>();
// do Async work
return rows;

I can see rows get filled with the Async web obtained data.

But the call to Scrape does not seem to load TeamRows2 with rows when it returns.

Do I need to cast from plain ObservableCollection from Task somehow for that to work?

Thanks

redt
  • 33
  • 6
  • Cast? No, no, no, no... What you want is the [Result](https://msdn.microsoft.com/en-us/library/dd321468(v=vs.110).aspx). Edit: I think you would be better without `async`. – Theraot May 30 '17 at 21:15
  • you need to wait for the task to finish. Either `await` or calling `.Result` on the task – Jonesopolis May 30 '17 at 21:24
  • 2
    Do **not** use Task.Result. Read the great blog of [Stephen Cleary](https://blog.stephencleary.com/) why you should not do that – Sir Rufo May 30 '17 at 21:29
  • I have to use async / await, as I am using: htmlDoc = await web.LoadFromWebAsync(url); I tied putting Scrape().Result; But it errors :( – redt May 30 '17 at 21:49
  • My VMs have an InitializeAsync( object parameter ) method and all initialization will happen there. The constructor only takes the dependency (if any) but did not initialize any data – Sir Rufo May 30 '17 at 22:00
  • 1
    Doing `async` work inside the constructor is a bad design decision. You have to call `await Scrape()` in a different method which is declared `async` outside your constructor. – Federico Dipuma May 30 '17 at 22:22

1 Answers1

2

As the comments already suggested you need to wait for the task to finish. There is the method to call Task.Result but there are some common cases where calling this method causes a deadlock in your application and everything comes to a stop.

As a general rule you can say: Never mix async/await with .Result/.Wait.

Now your main issue is that you are working inside a constructor and need to wait for your task to complete. The solution to this issue I prefer is a static factory method.

Like so:

public static async Task<YourClass> CreateAsync(…)
{
   var rows = await Scrape();
   return new YourClass(rows, …);
}

The constructor is set to private and when you need to create a instance of the class you call the CreateAsync method.

This solution assumes that the Scrape method is static as well. But it should be anyway if you call it from the constructor (or at least it should be easy to make it static, since all the parameters it could receive have to be passed to the constructor.

A alternative solution is using a initialization function. This would be the solution if you can't turn the Scape method static. Even in this case I would suggest a factory method, to avoid exposing instances of the class that are not properly initialized.

In this case you basically need two method like this:

public static async Task<YourClass> CreateAsync(…)
{
   var result = new YourClass(…);
   await result.InitializeAsync();
   return result;
}

private async Task InitializeAsync()
{
   TeamRows2 = await Scrape();
}
Nitram
  • 6,486
  • 2
  • 21
  • 32