9

I wonder if there's a better approach to load async data into a property. now I create an async function and raise a Task in the Get part of the property like this:

private ObservableCollection<CProyecto> prope;

public ObservableCollection<CProyecto> Prope
{
    get 
    {
        if (prope == null)
        {
            Task.Run(()=> LoadData()).Wait();
        }

        return proyectos;
    }
    set 
    { 
        prope = value; 
        RaisePropertyChanged(); 
    }
}

async private Task LoadData() 
{
    Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}

This approach works, but I don't like the use of .Wait, because that can freeze the screen if the service doesn´t respond fast.

Can you please guide me on this matter?

thanks in advance

5
  • 1
    What's the point of starting a new task if you call the Wait() method to block the calling thread anyway....? Commented Apr 12, 2017 at 12:01
  • Regarding async properties you should read this: blog.stephencleary.com/2013/01/async-oop-3-properties.html Commented Apr 12, 2017 at 12:02
  • Use a loaded event to fetch data from external source. Make the event async and bind to your collection. Commented Apr 12, 2017 at 12:14
  • 1
    Worst. Property. Ever. Commented Apr 12, 2017 at 14:05
  • Eldho, loaded_event? from the xaml page ? . thanks Commented Apr 12, 2017 at 19:59

3 Answers 3

12

The way I handled this was to start the process of loading the property when the object was constructed, but I did not await the result. Since the property notifies when it is populated, the bindings worked just fine. Essentially it works like this:

public class MyClass : INotifyPropertyChanged
{
    private ObservableCollection<CProyecto> prope;

    public ObservableCollection<CProyecto> Prope
    {
        get { return prope; }
        set { prope = value; RaisePropertyChanged(nameof(Prope)); }
    }

    public MyClass()
    {
        // Don't wait or await.  When it's ready
        // the UI will get notified.
        LoadData();
    }

    async private Task LoadData() 
    {
        Prope = await clsStaticClassDataLoader.GetDataFromWebService();
    }
}

This works very well, and does not cause any delays or stuttering in the UI. If you want the collection to never be null (a good practice IMO), you can pre-initialize the prope field with an empty collection.

Sign up to request clarification or add additional context in comments.

8 Comments

This is a good first step, but will silently swallow errors thrown by LoadData. A more comprehensive approach would have a top-level try/catch in LoadData and update your UI with an error notification.
Agreed. I am describing the approach I took. However, you have the correct location to do the error handling.
but you can not call async methods from a constructor, is the same problem with the property. You got this warning msdn.microsoft.com/en-us/library/hh873131.aspx
@KillemAll, you get the warning because you can't await the response. The goal here is not to await, but to start the process of loading the data. We get notified in another way that the work is done.
I did just like you said and the only way I got not warning is doing LoadData().wait(); or what I am missing?
|
12

I suggest you read my MSDN article on async MVVM data-binding. I have a library (github link) that provides a NotifyTask<T> type, which can be used as such:

public class MyClass : INotifyPropertyChanged
{
  public NotifyTask<ObservableCollection<CProyecto>> Prope { get; private set; }

  public MyClass()
  {
    // Synchronously *start* the operation.
    Prope = NotifyTask.Create(LoadDataAsync());
  }

  async private Task<ObservableCollection<CProyecto>> LoadDataAsync()
  {
    return await clsStaticClassDataLoader.GetDataFromWebService();
  }
}

Then your databinding would operate on Prope.Result.

The advantage of this approach is that you can also use databinding to hide/show busy indicators (Prope.IsNotCompleted), show controls when the data is available (Prope.IsSuccessfullyCompleted), and error notifications (Prope.IsFaulted / Prope.ErrorMessage).

Also, you can specify a non-null default value, if you wish:

Prope = NotifyTask.Create(LoadDataAsync(), new ObservableCollection<CProyecto>());

4 Comments

library is found on github now: github.com/StephenCleary/Mvvm.Async
Ok, dumb question, how do I load this library into my project? I'm using the old version of INotifyTaskCompletion and NotifyTaskCompletion.Create(). I found it on an old post of Stephen Cleary's and it was in a Nuget package of Nito.AsyncEx.Strong.
@FLAdmin: Sorry for the confusion; the asynchronous MVVM types I think have gone through more package renames than any other code I've written. They're currently at: nuget.org/packages/Nito.Mvvm.Async
Thanks so much Stephen. I'll import that package. I actually copied your NotifyTaskCompletion class from your article and it worked great. to keep me going. learn.microsoft.com/en-us/archive/msdn-magazine/2014/march/…
0

Your current implementation of the Prope property doesn't make much sense. It is pointless to execute the LoadData method on a background thread since you block the main thread anyway when you call Wait(). You might as well call Wait() directly on the task returned by the LoadData() method:

//BAD IMPLEMENTATION!
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
    get
    {
        if (prope == null)
            LoadData().Wait();
        return proyectos;
    }
    set { prope = value; RaisePropertyChanged(); }
}

The above implementation is still a bad one. The getter of a property is not supposed to perform an asynchronous operation. You should read @Stephen Cleary's blog post on the subject: https://blog.stephencleary.com/2013/01/async-oop-3-properties.html

...and look into his NotifyTaskCompletion type in his AsyncEx library: https://github.com/StephenCleary/AsyncEx

1 Comment

Well It looks like don´t have sense but actually it have sense, if I don't do that the system hangs when I call my webservice,. I guess that is because the way I call the service var Response = client.GetAsync(url).Result;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.