0

Please Note: This is continuation of the question posted earlier but the solution of interest is of a different situation.

I am trying to make multiple calls to the methods that each return IObservable but the values being returned back in the SelectMany statement is a Task and hence the following Subscribe statement does not compile.

This is the code snippet

 var myWorkList = new List<MyWork>
                {
                    new MyWork(),// MyWork.Execute(data) returns IObservable
                    new MyWork()
                }.ToObservable();

 var results =
   myService
    .GetData(accountId)
    .SelectMany(data => myWorkList.ForEachAsync(r => r.Execute(data))
    .Subscribe(result =>
    {
        Console.WriteLine($"Result Id: {result.Id}");
        Console.WriteLine($"Result Status: {result.Pass}");
    });
Tarc
  • 3,214
  • 3
  • 29
  • 41
Gautam T Goudar
  • 271
  • 3
  • 12

2 Answers2

1

You just want to use .SelectMany. Try this:

var myWorkList = new List<MyWork>()
{
    new MyWork(),
    new MyWork()
}.ToObservable();

var query =
    from data in myService.GetData(accountId)
    from myWork in myWorkList
    from result in myWork.Execute(data)
    select result;

var results =
    query
        .Subscribe(result =>
        {
            Console.WriteLine($"Result Id: {result.Id}");
            Console.WriteLine($"Result Status: {result.Pass}");
        });

Here's my testing code:

public static class myService
{
    public static IObservable<MyData> GetData(int x)
        => Observable.Return(new MyData());
}

public class MyWork
{
    public virtual IObservable<MyResult> Execute(MyData data)
    {
        return
            from isMatch in IsMatch(data)
            where isMatch
            select new MyResult() { Id = 1, Pass = true };
    }

    public IObservable<bool> IsMatch(MyData data)
    {
        return Observable.Return(true);
    }
}

public class MyResult
{
    public int Id;
    public bool Pass;
}

public class MyData { }

When I execute I get this:

Result Id: 1
Result Status: True
Result Id: 1
Result Status: True

In the comments on your previous question I suggested doing this as a list of delegates. Here's how:

var myWorkList = new Func<MyData, IObservable<MyResult>>[]
{
    md => new MyWork().Execute(md),
    md => new MyWork().Execute(md),
}.ToObservable();

var query =
    from data in myService.GetData(accountId)
    from myWork in myWorkList
    from result in myWork(data)
    select result;

You get the same result.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
1

The list can be declared just as list of MyWork-s - using no ToObservable here.

var myWorkList = new List<MyWork>
            {
                new MyWork(),// MyWork.Execute(data) returns IObservable
                new MyWork()
            };

then, we map the objects, returned by myService.GetData onto the elements of myWorkList and take them as IObservable-s.

var observables = myService
         .GetData(accountId)
         .SelectMany(data => myWorkList.Select(r => r.Execute(data)));

And now you can observe them.

Either together - merged:

var subscription =
          observables
           .Merge()
           .Subscribe(result =>
           {
               ...
           });

Or separately:

var subscriptions=
          observables
           .Select(obs => 
               obs.Subscribe(result =>
               {
                   ...
               }))
           .ToArray();

Update: the latter case must be immediately materialized in order to prevent side effects (.ToArray()).

armenm
  • 922
  • 6
  • 9
  • The "Or separately" option is terrible. Don't do `.Subscribe` in a `.Select` life that. That's horrible. Had you written the line `.SelectMany(data => myWorkList.Select(r => r.Execute(data)));` like `.SelectMany(data => myWorkList.SelectMany(r => r.Execute(data)));` you would have avoided the need for `.Merge` or any funky inner subscribes. – Enigmativity May 07 '18 at 00:20
  • @Enigmativity, I updated my answer to make it safer. In the case of "Merge" you have the option to have only one subscription. In many cases it's a lot more usable than having many separate ones. – armenm May 07 '18 at 09:46
  • "In many cases"? In every case. Nested subscribes are terrible. And what do you mean by "the latter case must be immediately materialized in order to prevent side effects"? – Enigmativity May 07 '18 at 10:42
  • There's nothing wrong with it - the way I wrote it will work perfectly. I have an IEnumerable of IObservable-s which I convert to an array of subscriptions (IDisposables). Please, if you use words like "terrible" be so kind to explain what you exactly mean with it. First of all, semantically, and secondarily, syntactically. – armenm May 07 '18 at 11:21
  • @Enigmativity, .SelectMany(data => myWorkList.SelectMany(r => r.Execute(data))) does not work because the compiler cannot infer the types for SelectMany. But I do agree that SelectMany operates the same way as calling Select and Merge. – Gautam T Goudar May 07 '18 at 14:47
  • @GautamTGoudar - I just tested `.SelectMany(data => myWorkList.SelectMany(r => r.Execute(data)))` and it works perfectly fine. – Enigmativity May 07 '18 at 23:51
  • @Enigmativity. Yes it works. For this I had to make myWorkList to be an Observable. var myWorkList = new List { new MyWork(), new MyWork() }.ToObservable(); – Gautam T Goudar May 08 '18 at 15:38