Миграция асинхронной команды ReactiveUI с 4.6.1 на 7.0
Как я могу преобразовать это в ReactiveCommand из 7.0:
private void SetCanRunProcessCommand()
{
_runProcessCommand = new ReactiveAsyncCommand(CanRunProcess(null));
_runProcessCommand.RegisterAsyncAction(RunProcess);
_runProcessCommand.ThrownExceptions.Subscribe(OnError);
_runProcessCommand.AsyncStartedNotification.Subscribe(_ => IsBusy = true);
_runProcessCommand.AsyncCompletedNotification.Subscribe(x => IsBusy = false);
_runProcessCommand.AsyncCompletedNotification.Subscribe(_ => PerformPostRunImpl());
}
Вот что я пытаюсь:
private void SetCanRunProcessCommand()
{
_runProcessCommand = ReactiveCommand.CreateFromTask((x) => Run(x), CanRunProcess(null));
_runProcessCommand.IsExecuting.ToProperty(this, x => x.IsBusy);
_runProcessCommand.ThrownExceptions.Subscribe(OnError);
}
private async Task Run(object param)
{
try
{
await RunProcess(param);
}
finally
{
PerformPostRunImpl();
}
}
Проблема этого подхода в том, что я не могу сейчас управлять планировщиком. Когда я тестирую код, я не могу использовать TestScheduler
двигаться вперед во времени, потому что код не зависит от планировщика.
Второй подход заключается в передаче планировщика для использования ReactiveCommand.Create
с RxApp.ThreadPoolScheduler
который будет CurrentThread в модульных тестах. И вот как я бы это сделал, но есть и другие проблемы.
- Похоже, что планировщик не контролирует RunProcess (он всегда вызывается в основном потоке). я думал так
ReactiveCommand.Create(function)
равно этому в предыдущей версии:
Код:
var command = ReactiveCommand.Create();
command.Subscribe(function);
Но это не потому, что второй вызов контролируется планировщиком.
Это создаст еще одну проблему, потому что
RunProcess
содержит некоторые вызовы, которые необходимо выполнить в потоке пользовательского интерфейса, поэтому я получу исключение между потоками. Как это работает в 4.6.1 ReactiveUI? Как я могу вернуться к такому поведению?Как мне выполнить операцию после
RunProcess
? Наконец-то, похоже, не работает.
Простой пример:
public bool Validation()
{
return isValid(); Background
}
public void RunProcess()
{
LongRunning(); // Background
}
public void SetEverythingIsCorrect()
{
State = Success; // UI
}
Мне нужно создать ReactiveCommand, который будет вызывать эти 3 метода на разных планировщиках. В тестах я хочу использовать TestScheduler, поэтому все должно быть однопоточным. Как это:
new TestScheduler().With(sched =>
{
Abc a = new Abc(_dialogService, _fileSystemHelper);
a.RunProcessCommand.Execute();
sched.AdvanceBy(1000);
Assert.AreEqual(State.Success, a.State);
});
Проверка,RunProcess должен быть вызван TaskPoolScheduler SetEverythingIsCorrect должен быть вызван на MainThreadScheduler.
Мой последний подход был чем-то похожим на это:
protected internal async Task RunProcess(object obj)
{
await Observable.Start(Validation, RxApp.TaskpoolScheduler)
.Concat(Observable.Start(() => RunProcess(obj), RxApp.TaskpoolScheduler))
.Concat(Observable.Start(SetEverythingIsCorrect, RxApp.MainThreadScheduler))
.Finally(() =>
{
PerformPostRunImpl();
IsBusy = false;
});
}
_runProcessCommand = ReactiveCommand.CreateFromTask(() => RunProcess(null), CanRunProcess(null));