0

I have the following ViewModel structure:

  • App.xaml.cs
    • MainViewModel
      • DetailViewModel

I navigate between MainViewModel and DetailViewModel by this code:

navigationService.For<DetailViewModel>().Navigate(); 

It is the constructor of DetailViewModel:

public DetailViewModel(INavigationService navigationService) { GetData(); } private async Task GetData() { using (var context = new MyDataContext()) { var result = await (from data in context.Data select data).ToListAsync(); DataList = new ObservableCollection<Data>(result); } } 

As I don't use await when I call GetData, the constructor should return very quickly and the list should be populated later.

I experience a very slow navigation, I click the item in the MainViewModel, the GUI freezes for a second and I see the Detail when the list is populated (debug shows that the constructor is completed before the list is done).

I see a warning that says:

Because this call is not awaited, execution of the current method continues before the call is completed.

What blocks then? What else should I set?

7
  • 1
    i think because async does not create new thread. so it will run in main thread so GUI freezes. you have to use Task.Run() to run it on another thread. Commented Nov 26, 2015 at 8:44
  • How big is the data collection that is being populated? Commented Nov 26, 2015 at 8:44
  • @M.kazemAkhgary How could be async if the same thread is used? I don't think so. Commented Nov 26, 2015 at 8:46
  • 1
    Marking a method async does not mean it executes on a background thread. This entire method is executed in the UI thread. Commented Nov 26, 2015 at 8:47
  • 1
    Then you need to check if the SQLite driver is actually making an asynchronous call to the database. Commented Nov 26, 2015 at 8:48

3 Answers 3

2

AFAIK, the SQLite Entity Framework provider does not support true asynchronous methods. So, the call to ToListAsync is in fact synchronous, causing your delay.

The best way to solve this is to use Task.Run with await (not ContinueWith or Execute.OnUIThread):

public DetailViewModel(INavigationService navigationService) { InitializeAsync(); } private async Task InitializeAsync() { try { DataList = await Task.Run(() => GetData()); } catch { // TODO: Log } } private List<Data> GetData() { using (var context = new MyDataContext()) { return context.Data.ToList(); } } 

Note the introduction of a try/catch; error handling here is necessary since InitializeAsync is being treated as a "top-level" asynchronous operation.

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

1 Comment

Thanks for the improved answer. I modified my code according to this (I had to add the async keyword to the InitializeAsync method).
0

Replace your code with this one instead:

public DetailViewModel(INavigationService navigationService) { Task.Run(() => { GetData(); }); } private void GetData() { using (var context = new MyDataContext()) { var result = await (from data in context.Data select data).ToListAsync(); DataList = new ObservableCollection<Data>(result); } } 

1 Comment

This way I get a new thread but the DataList items are not shown in the View if I use this approach.
-1

This way it works as expected:

public DetailViewModel(INavigationService navigationService) { Task.Run(() => { GetData(); }).ContinueWith( (x) => { Execute.OnUIThread( () => DataList = new ObservableCollection<Data>(x.Result)); }); }; } private Task<List<Data>> GetData() { using (var context = new MyDataContext()) { return await (from data in context.Data select data).ToListAsync(); } } 

Thanks for the advices.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.