0

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.

  1. 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.

  1. 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?

  2. 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));
Yael
  • 1,566
  • 3
  • 18
  • 25
MistyK
  • 6,055
  • 2
  • 42
  • 76
  • Is this definitely v7? Because it's not released yet as far as I'm aware. It's not clear how you were 'controlling the scheduler' in the v4 code - there's no mention of it. Re 1, do you mean `CreateFromTask`? The scheduler you pass controls scheduling of *results* (i.e. `ObserveOn`). Re 3, there should be no issue with `finally`. – Charles Mager May 16 '16 at 14:17
  • 1. By controlling the scheduler I mean use TestScheduler to override RxApp.MainThreadScheduler and RxApp.TaskPoolScheduler then RunUnit tests without any threading. 1. Scheduling of results - which function is a result function. Is it 'function' passed into 'Create' function or 'Subscribe' function? 2. Should it be invoked after 'Execute' finishes the work? I thought that 'finally' doesn't make sense in case of RelayCommand because it should be invoked when observable collection is processed completely (which should never happen) not after every call to execute – MistyK May 16 '16 at 14:22
  • Yes, but you *aren't* controlling any scheduling in the original code, so what are you trying to do now? By default it will use `RxApp.MainThreadScheduler` for observing results. This sounds like what you'd want. – Charles Mager May 16 '16 at 14:25
  • 1
    We really need a [mcve] that demonstrates the behaviour your seeing now and an explanation of the behaviour you'd like. As it stands, this question isn't very clear. – Charles Mager May 16 '16 at 14:26
  • @CharlesMager hey, I have updated the post – MistyK May 17 '16 at 13:23

0 Answers0