2

In my WPF applciation, my ViewModel, talks to a ViewModelService, which in turn talks to say for example a CalculationService to retrieve processed data to be presented in the View. I would like for the view to be able to report percentage of completion to the user. My question is two fold.

The first is where should this logic reside? Should the CalculationService be tasked with reporting progress along with the actual results or does the ViewModel service calculate the progress percentage details.

Second, I see that I can achieve this using TPL or using ReactiveExtentions. Am leaning towards reactive extentions becuase in my case, the calculation service's API and implementation looks like this

//This is pseudo code, just aiming to show my intention here
    IEnumerable<Output> Calculate(IEnumerable<Inputs> inputs){
             List<Output> output = new List<Output>();
             foreach(var input : inputs){
               //long running calculation, which produces output
               //outputs.Add(output)
             }
           }

Am assuming that if I go down the Reactive route, I can use Reactive's OnNext to stream the "Output", as they arrive and I also can use Parallel.Foreach to gain performance.

Any ideas or thoughts on this?

Thanks Kay

supertopi
  • 3,469
  • 26
  • 38
Karthik Balasubramanian
  • 1,127
  • 4
  • 13
  • 36

1 Answers1

1

I think it is fine for the view model layer to calculate progress.

Here's how I would do it with Rx:

First create a Progress<T> class to lift Output into a type that can report its own progress.

public class Progress<T>
{
    public Progress(T value, int current, int total)
    {
        this.Value = value;
        this.Current = current;
        this.Total = total;
    }
    public T Value { get; private set; }
    public int Current { get; private set; }
    public int Total { get; private set; }
    public double Percentage
    {
        get
        {
            return (double)this.Current / (double)this.Total;
        }
    }
}

Now you just need the Calculate method.

public IObservable<Progress<Output>> Calculate(IEnumerable<Inputs> inputs)
{
    return
        Observable
            .Create<Progress<Output>>(o =>
            {
                var source = inputs.ToArray();
                var total = source.Length;
                return
                    source
                        .ToObservable()
                        .Select((x, n) =>
                            new Progress<Output>(LongRunningCalculation(x), n, total))
                        .Subscribe(o);
            });     
}

This doesn't do any performance improvement, but I certainly wouldn't use Parallel.ForEach to get any improvement. Mixing tasks, enumerables, and observables isn't the best. It's always better to stick to one paradigm.

Here's how I would make this run in parallel.

IObservable<Progress<Output>> Calculate(IEnumerable<Inputs> inputs)
{
    return
        Observable
            .Create<Progress<Output>>(o =>
            {
                var source = inputs.ToArray();
                var total = source.Length;
                return
                    source
                        .ToObservable()
                        .SelectMany(x => Observable.Start(() => LongRunningCalculation(x)))
                        .Select((x, n) => new Progress<Output>(x, n, total))
                        .Subscribe(o);
            });     
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172