Execute a set of commands in order, sequentially w

2019-07-19 05:08发布

问题:

I have a ViewModel with some ReactiveCommands that user can invoke manually.

CommandA
CommandB
CommandC

So the user can invoke only B, or A first, and then C.

The problem is that there is that I want to create another command that runs them in order, from A to C. They shouldn't run in parallel, but sequentially.

How can I do a command of the given characteristics?

回答1:

Import System.Reactive.Linq, now you can:

{
    await CommandA.Execute();
    await CommandB.Execute();
    await CommandC.Execute();
}

System.Reactive.Linq makes IObservable as awaitable.



回答2:

Since you want them executed sequentially, ReactiveCommand.CreateCombined isn't really a good fit. However, you could create something similar to CombinedReactiveCommand to work for your situation.

Reactive commands can be executed manually by calling ReactiveCommand<TParam,TResult>.Execute, which returns an IObservable<TResult> that returns a single value. Therefore, a very basic implementation could just chain it to other execute calls with SelectMany:

ReactiveCommand.CreateFromObservable(() =>
    CommandA.Execute()
        .SelectMany(_ => CommandB.Execute())
        .SelectMany(_ => CommandC.Execute()));

If all of your commands have the same type, it could be made more generic:

var commands = new[] { CommandA, CommandB, CommandC };
ReactiveCommand.CreateFromObservable(() =>
    commands.Aggregate(
        Observable.Return(Unit.Default),
        (acc, cur) => acc.SelectMany(_ => cur.Execute())));

However, this does not take into consideration the other things CombinedReactiveCommand handles, such as CanExecute and ThrownExceptions. If you need to handle those, then you might consider rolling your own CombinedReactiveCommand.



回答3:

I am asuming, that your commands are of type ReactiveCommand<Unit,Unit> so, you can do this:

public class MyCoolViewModel
{
   bool runSequentially = false;

   public ReactiveCommand<Unit, Unit> Command { get; set; }
   public ReactiveCommand<Unit, Unit> Command2 { get; set; }
   public ReactiveCommand<Unit, Unit> Command3 { get; set; }
   public ReactiveCommand<Unit, Unit> CommandwhoInvokesOtherCommands { get; set; }


   public MyCoolViewModel()
   {

       Command = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
       {    
        runSequentially= false;
        Console.WriteLine("Start 1");
        await Task.Delay(1000);
        Console.WriteLine("End 1");
        return Unit.Default;
       });
      Command2 = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
      {
        runSequentially = false;
        Console.WriteLine("Start 2");
        await Task.Delay(1000);
        Console.WriteLine("End 2");
        return Unit.Default;
      });

    Command3 = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
    {
        Console.WriteLine("Start 3");
        await Task.Delay(1000);
        Console.WriteLine("End 3");
        return Unit.Default;
    });

    CommandwhoInvokesOtherCommands = ReactiveCommand.CreateFromTask<Unit, Unit>(async _ =>
      {
          Console.WriteLine("Invoking other commands");
          runSequentially = true;
          await Task.Delay(1000);
          Console.WriteLine("End");
          return Unit.Default;
      });

            /*Command 1, 2 and 3 only will run if flag is set to true*/
    CommandwhoInvokesOtherCommands.Where(_ => runSequentially).InvokeCommand(Command);
    Command.Where(_ => runSequentially).InvokeCommand(Command2);
    Command2.Where(_ => runSequentially).InvokeCommand(Command3);

    //Observable.Return(Unit.Default).InvokeCommand(CommandwhoInvokesOtherCommands);//for test purposes

}
}

The utility method InvokeCommand will call another command when a command returns, so this method plus a where clause will do the trick.

I hope this help you Regards