Comando Async do ReactiveUI 6 não está sendo executado no segmento de segundo plano no aplicativo WPF
ViewModel
public class MyViewModel:ReactiveObject, IRoutableViewModel{
private ReactiveList<string> _appExtensions;
public MyViewModel(IScreen screen){
HostScreen = screen;
AppExtensions = new ReactiveList<string>();
GetApplicationExtensions =
ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable<string>>
GetApplicationExtensions
.ObserveOn(RxApp.MainThreadScheduler)
.SubscribeOn(RxApp.TaskpoolScheduler)
.Subscribe(p =>
{
using (_appExtensions.SuppressChangeNotifications())
{
_appExtensions.Clear();
_appExtensions.AddRange(p);
}
});
GetApplicationExtensions.ThrownExceptions.Subscribe(
ex => Console.WriteLine("Error during fetching of application extensions! Err: {0}", ex.Message));
}
// bound to a ListBox
public ReactiveList<string> AppExtensions
{
get { return _appExtensions; }
set { this.RaiseAndSetIfChanged(ref _appExtensions, value); }
}
public ReactiveCommand<IEnumerable<string>> GetApplicationExtensions { get; protected set; }
}
e o View tem um<Button Command="{Binding GetApplicationExtensions}"></Button>
.
implentação deGetApplicationExtensions
public async Task<IEnumerable<string>> GetApplicationExtensions()
{
IEnumerable<string> extensions = null;
using (var client = new HttpClient())
{
client.BaseAddress = BaseAddress;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
var response = await client.GetAsync("applications");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
extensions = JsonConvert.DeserializeObject<IEnumerable<string>>(json);
}
}
return extensions;
}
De tudo o que li sobre o ReactiveUI e todos os exemplos que já vi (embora existam muito poucos para as novas versões 6.0+), isso deve fazer minha chamada assíncrona (que faz uma solicitação HTTP assíncrona viaHttpClient
) executar em um encadeamento em segundo plano e atualizar umListBox
na minha opinião, quando os resultados são retornados a partir dele. No entanto, esse não é o caso - a interface do usuário fica bloqueada durante a chamada assíncrona. O que estou fazendo errado?
Se eu encerrar minha chamada HTTP em umTask
tudo funcionou como planejado - a interface do usuário não desligou. Portanto, a nova implementação da minha chamada de serviço é esta:
public Task<IEnumerable<string>> GetApplicationExtensions()
{
var extensionsTask = Task.Factory.StartNew(async () =>
{
IEnumerable<string> extensions = null;
using (var client = new HttpClient())
{
client.BaseAddress = BaseAddress;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
var response = await client.GetAsync("applications");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
extensions = JsonConvert.DeserializeObject<IEnumerable<string>>(json);
}
}
return extensions;
}
return extensionsTask.Result;
}
Além disso, com essa alteração na minha chamada de serviço assíncrona, eu poderia remover oObserveOn
eSubscribeOn
do meuReactiveCommand
como @PaulBetts sugeriu. Então meuReactiveCommand
implementação no construtor do meu modelo de exibição tornou-se o seguinte:
GetApplicationExtensions =
ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable<string>>
GetApplicationExtensions
.Subscribe(p =>
{
using (_appExtensions.SuppressChangeNotifications())
{
_appExtensions.Clear();
_appExtensions.AddRange(p);
}
});