#c# #asynchronous #xamarin #xamarin.forms #prism
#c# #асинхронный #xamarin #xamarin.forms #призма
Вопрос:
Я делаю запросы к Github Api, поэтому у меня есть асинхронные методы, они выполняют эту работу. Перед этим я всегда вызывал they в методе, который вызывает из command(фактически DelegateCommand ). Но теперь я хочу выполнить запрос в ViewModel, потому что мне нужно отобразить список на странице. Я использую Prism для подключения view и viewmodel. Поскольку я не могу сделать viewmodel асинхронным, я не могу использовать await word, поэтому я попытался сделать что-то вроде получения результата из task или task.wait. Но с этим я получаю тот же результат. Моя остановка приложения работает с белым дисплеем, когда он запрашивал. Я прочитал некоторую информацию об этом и понял, что вызов асинхронного метода в не асинхронном методе плохой, и это вызывает взаимоблокировку, но я не знаю, что с этим делать. И я думаю, что взаимоблокировка приводит к тому, что приложение перестает работать. Вот метод, в котором приложение умирает:
public async Task<IEnumerable<RepositoryModel>> GetRepositoriesAsync()
{
try
{
var reposRequest = new RepositoryRequest { Sort = RepositorySort.FullName };
var gitHubRepos = await _gitHubClient.Repository.GetAllForCurrent(reposRequest); //async request, don't say about name convention, it is not my method.
var gitRemoteRepos = new List<RepositoryModel>();
foreach ( var repository in gitHubRepos )
{
var repos = new RepositoryModel();
repos.RepositoryTypeIcon = GetRepositoryTypeIcon(repository);
gitRemoteRepos.Add(repos);
}
return gitRemoteRepos;
}
catch ( WebException ex )
{
throw new Exception("Something wrong with internet connection, try to On Internet " ex.Message);
}
catch ( Exception ex )
{
throw new Exception("Getting repos from github failed! " ex.Message);
}
}
И вот viewmodel:
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public ObservableCollection<RepositoryModel> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
var task = _repositoriesManager.GetRepositoriesAsync();
//task.Wait();
Repositories = task.Result as ObservableCollection<RepositoryModel>;
}
}
Комментарии:
1. Не используйте TPL в конструкторе
2. Не используйте
Task.Result
из основного потока (GUI).3. blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Ответ №1:
Я рекомендую использовать my NotifyTask<T>
type, который предоставляет оболочку с возможностью привязки к данным Task<T>
. Я более подробно объясняю этот шаблон в своей статье об асинхронной привязке данных MVVM.
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public NotifyTask<ObservableCollection<RepositoryModel>> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
Repositories = NotifyTask.Create(GetRepositoriesAsync());
}
}
private async Task<ObservableCollection<RepositoryModel>> GetRepositoriesAsync()
{
return new ObservableCollection<RepositoryModel>(await _repositoriesManager.GetRepositoriesAsync());
}
Обратите внимание, что при таком подходе ваша привязка данных будет использоваться Repositories.Result
для доступа к фактической коллекции. Также доступны другие свойства, в первую очередь Repositories.IsCompleted
и Respositories.IsNotCompleted
для отображения / скрытия занятых блесен.
Комментарии:
1. Большое вам спасибо. И для ваших статей тоже.