How can I convert this into ReactiveCommand from 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());
}
Here is what I am trying:
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();
}
}
The problem with this approach is that I can't control scheduler now. When I test the code I cannot use TestScheduler
to move forward in time because code is not scheduler dependent.
The second approach is to pass scheduler to use ReactiveCommand.Create
with RxApp.ThreadPoolScheduler
which will be CurrentThread in unit tests. And this is how I would do it but there are other problems with it.
- It looks like scheduler doesn't control RunProcess (it's always invoked on main thread). I thought that
ReactiveCommand.Create(function)
is equal to this in the previous version:
Code:
var command = ReactiveCommand.Create();
command.Subscribe(function);
But it's not because the second call is controlled by scheduler.
This would then create another problem because
RunProcess
contains some calls that need to be made on the UI Thread so I will get cross-thread exception. How did it work in 4.6.1 ReactiveUI? How can I get back to that behaviour?How do I perform an operation after
RunProcess
? Finally doesn't seem to work.
Simple example:
public bool Validation()
{
return isValid(); Background
}
public void RunProcess()
{
LongRunning(); // Background
}
public void SetEverythingIsCorrect()
{
State = Success; // UI
}
I need to create a ReactiveCommand that will invoke these 3 methods on different schedulers. In tests I want to use TestScheduler so everything should be single-threaded. Like this:
new TestScheduler().With(sched =>
{
Abc a = new Abc(_dialogService, _fileSystemHelper);
a.RunProcessCommand.Execute();
sched.AdvanceBy(1000);
Assert.AreEqual(State.Success, a.State);
});
Validation,RunProcess should be invoked TaskPoolScheduler SetEverythingIsCorrect should be invoked on a MainThreadScheduler.
My last approach was something similiar to this:
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));