#c_sharp #wpf #async_await
У меня имеется WPF-приложение, работающее с базой данных. Количество записей в базе довольно большое и постоянно растёт. В проекте используется ORM(EF 6). Имеется некий класс, работающий непосредственно с контекстом базы данных: public class Store : IStore {...} В интерфейсе класса определён ряд методов, код в которых обращается непосредственно к базе через EF-контекст, соответственно, выполнение метода занимает довольно длительное время, что может вызвать простой интерфейса пользователя, если я всё правильно понимаю. Следовательно, нужно вынести операции получения данных из базы в отдельные потоки. Так, скажем, в Store определен метод: public ICollectionGetAllProducts() {...} Если я правильно понимаю, мне необходимо добавить его асинхронную реализацию: 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)); (но здесь я не уверен, т. к. ни разу не пробовал).
Комментариев нет:
Отправить комментарий