Как сделать асинхронное неблокирующее чтение с ожиданием результата?

Есть задача: загрузить данные из таблицы БД в DataTable. Так как данных достаточно много и сервер зело задумчивый, собираюсь воспользоваться асинхронными методами, но почему-то ничего не работает как заявлено. Есть такой код:

public void LoadData()
{
 LoadDataAsync();
}

protected async Task LoadDataAsync()
{
  // какой-то подготавливающий код - неинтересные, скучные подробности
  await _dataAdapter.Fill(_dataTable);
  // другой код, установка переменных и прочие действия
}

После вызова LoadData() вторая часть метода LoadDataAsync() просто не выполняется и выполнение передается в функцию, которая вызывала LoadData(). И, значит, таблица пуста. Пробовал так:

public async void LoadData()
{
 await LoadDataAsync();
}

Создавал Task-и и вызывал через Run с await-ом и через await Task.WhenAll(t) в обоих методах, один делал с префиксом async, второй нет, пробовал разные их комбинации. Пока добился ожидания следующим образом:

public void LoadData()
{
 Task _res = Task.Run(LoadDataAsync);
 Task.WaitAll(_res);
}

Код для LoadDataAsync() как показан выше.

Отсюда вопрос: как сделать корректно загрузку данных, чтобы не было блокировки и организовать ожидание загрузки данных в память?

Спасибо


Ответы (1 шт):

Автор решения: aepot

На данный момент эта функция вызывается из конструктора.

В конструктор нельзя вставлять свой код из-за всякого рода ограничений, связанных с не до конца проинициализированным окном на момент выполнения конструктора. Вместо конструктора используйте событие Window.Loaded и его обработчик, и в нём уже можно сразу использовать асинхронность.

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    // здесь можно сделать что-то до начала загрузки
    await LoadDataAsync();
    // здесь что-то после, можно взаимодействовать с контролами
}

private async Task LoadDataAsync()
{
    // ...
    await _dataAdapter.Fill(_dataTable);
    // ...
}

Перенесите весь свой код из конструктора в обработчик события Window.Loaded, оставьте в конструкторе только родной InitializeComponent().

Еще почитайте эту статью.


Если же вы просто хотите запустить параллельную загрузку, то это можно сделать без асинхронности, а просто запуском задачи.

private void LoadData()
{
    // ...
    _dataAdapter.Fill(_dataTable).Wait();
    // ...
}

Вызов

Task.Run(() => LoadData());
→ Ссылка