ReactiveUI 6 Async-Befehl wird im Hintergrund-Thread in der WPF-App nicht ausgeführt
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; }
}
und die Aussicht hat eine<Button Command="{Binding GetApplicationExtensions}"></Button>
.
Umsetzung vonGetApplicationExtensions
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;
}
Nach allem, was ich über ReactiveUI gelesen habe und nach allen Beispielen, die ich gesehen habe (obwohl es für die neuen 6.0+ -Versionen extrem wenige gibt), sollte dies mein asynchroner Aufruf sein (der eine asynchrone HTTP-Anfrage über macht)HttpClient
) auf einem Hintergrundthread ausführen und aktualisieren aListBox
aus meiner Sicht, wenn die Ergebnisse daraus zurückgegeben werden. Dies ist jedoch nicht der Fall - die Benutzeroberfläche wird für die Dauer des asynchronen Aufrufs gesperrt. Was mache ich falsch?
Wenn ich meinen HTTP-Anruf in einTask
dann funktionierte alles wie beabsichtigt - die Benutzeroberfläche legte überhaupt nicht auf. Die neue Implementierung meines Serviceabrufs lautet also:
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;
}
Mit dieser Änderung an meinem asynchronen Serviceabruf konnte ich auch den entfernenObserveOn
undSubscribeOn
Von meinemReactiveCommand
wie @PaulBetts suggessted. So meinReactiveCommand
Die Implementierung im Konstruktor meines Ansichtsmodells lautete wie folgt:
GetApplicationExtensions =
ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable<string>>
GetApplicationExtensions
.Subscribe(p =>
{
using (_appExtensions.SuppressChangeNotifications())
{
_appExtensions.Clear();
_appExtensions.AddRange(p);
}
});