Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Асинхронный вызов метода в WPF

#c_sharp #wpf #async_await


У меня имеется WPF-приложение, работающее с базой данных. Количество записей в базе
довольно большое и постоянно растёт. В проекте используется ORM(EF 6).
Имеется некий класс, работающий непосредственно с контекстом базы данных:

public class Store : IStore {...}


В интерфейсе класса определён ряд методов, код в которых обращается непосредственно
к базе через EF-контекст, соответственно, выполнение метода занимает довольно длительное
время, что может вызвать простой интерфейса пользователя, если я всё правильно понимаю.
Следовательно, нужно вынести операции получения данных из базы в отдельные потоки.
Так, скажем, в Store определен метод:

public ICollection GetAllProducts() {...}


Если я правильно понимаю, мне необходимо добавить его асинхронную реализацию:

public async Task> GetAllProductsAsync()
{
    return await Task>.Factory.StartNew(GetAllProducts)
}


Как правильно воспользоваться таким методом в самом приложении в ViewModel, Скажем,
чтобы, пока данные загружаются, в StatusBar отображался прогресс загрузки данных, а
DataGrid отобразил результат как только данные загрузятся?
    


Ответы

Ответ 1



Окей, давайте начнём с Task.Factory.StartNew. Это нужно только если ваш запрос к базе данных не поддерживает асинхронность сам, и требует выделения отдельного потока (кстати, лучше писать просто Task.Run). Для свежего Entity Framework это не так, асинхронные функции поддерживаются правильно, из коробки: Entity Framework tutorial: async query and save. С асинхронностью на уровне базы данных вам не нужно создавать отдельные потоки. По поводу прогресса, с этим хуже. EF не поддерживает информацию о прогрессе операции, так что вы можете просто вывести состояние «читаю», и считывать до тех пор, пока не закончите. В случае, когда/если будет имплементирована поддержка прогресса, вам можно будет воспользоваться интерфейсом IProgress. Таким образом, код в VM будет выглядеть так: IsLoading = true; var localData = await model.LoadDataAsync(); IsLoading = false; Data = localData; Скорее всего, вы не захотите выкладывать модельные классы для View, поэтому вам понадобится обёртка, создающая VM-объекты для ваших entity: // в модели IQueryable GetData(); // в VM IsLoading = true; var localData = new List(); await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity)); IsLoading = false; Data = localData; Ну и, как верно отмечает @Pavel Mayorov, возможно вы захотите ловить исключения, так что вам понадобится try: IsLoading = true; var localData = new List(); try { await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity)); Data = new ObservableCollection(localData); } catch { IsFailed = true; throw; } finally { IsLoading = false; } Или, если вы хотите, чтобы данные появлялись не все вместе, а по мере подгрузки, наверное подойдёт просто await model.GetData().ForEachAsync(entity => Data.Add(new EntityVM(entity)); (но здесь я не уверен, т. к. ни разу не пробовал).

Комментариев нет:

Отправить комментарий