Question

Apologies if this question sounds a bit generic. I'm just looking for some general advice about how to go about solving this problem :)

Right now I have the following async method which is retrieving a list of files in a custom FileService. This is being fired in the Constructor of a ViewModel.

private async void Construct()
{
   Files = new ObservableCollection<FileViewModel>();
   IList _files = await _fileRepository.GetFiles();
   foreach (File file in _files)
   {
      Files.Add(new FileViewModel(file));
   }
}

As you can probably work out, the async method is retrieving the list of files in 1 step, and then adding them to list (which is the binding source in the UI) to another.

To the user, they will see nothing until the async process has finished, and then after a time they will see all the results at once.

In my head, this defeats the point of async as what I would really want is the results being added as they are being retrieved.

To add another step, I also have a linq query which pulls the first letter of each file to group in to categories. This executes after the foreach loop. How would I go about adding this step also?

Thank you very much for any adivce you can give!

Was it helpful?

Solution

You'll have to change your repository at a fundamental level in order to do this. You could return an object which fires events when the items are retrieved and then in the event handler add your object to an INotifyCollectionChanged interface implementation which your control is bound to.

You could also look into the Reactive Extensions and expose an IObservable<T> interface implementation which when subscribed to, would add the item to the data source (again, implementing INotifyCollectionChanged).

Regardless of which approach you take, you have to take into account the fact that UI components an only be updated on the thread that they are created on, meaning you have to marshal the call to the UI thread.

The easiest way to do this to capture the SynchronizationContext using the static Current property before you make any asynchronous calls and then call the Post method with the call to add the item to the INotifyCollectionChanged implementation.

async/await doesn't really lend itself to use in this situation; it's not well-suited for notifications when an operation is complete but to weave continuations on asynchronous operations when they're complete.

You might want to reconsider these approaches though. With most LINQ providers, an IQueryable<T> is returned, which derives from IEnumerable<T>. While there certainly can be blocking while the IEnumerable<T> is iterated through, most providers stream the results in rather quick succession, chances are too quick to give you the effect that you want.

If the LINQ provider is providing them too slowly (large gaps between each iteration), then you can use the ToObservable extension method in the Reactive Framework to enable one of the patterns mentioned above to give you the items to place in your list as soon as you get them.

OTHER TIPS

You'll need to change your file repository to do this.

I recommend using TPL Dataflow: have the file repository expose a BufferBlock<File> that it will start filling, and have your code consume it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top