0

How to get the individual API call status success response in C#.

I am creating a mobile application using Xamarin Forms,

In my application, I need to prefetch certain information when app launches to use the mobile application.

Right now, I am calling the details like this,

    public async Task<Response> GetAllVasInformationAsync()
    {
        var userDetails = GetUserDetailsAsync();
        var getWageInfo = GetUserWageInfoAsync();
        var getSalaryInfo = GetSalaryInfoAsync();


        await Task.WhenAll(userDetails,
            getWageInfo,
            getSalaryInfo,
          );

        var resultToReturn = new Response
        {
            IsuserDetailsSucceeded = userDetails.Result,
            IsgetWageInfoSucceeded = getWageInfo.Result,
            IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
        };

        return resultToReturn;
    }

In my app I need to update details based on the success response. Something like this (2/5) completed. And the text should be updated whenever we get a new response.

What is the best way to implement this feature? Is it possible to use along with Task.WhenAll. Because I am trying to wrap everything in one method call.

Sooraj
  • 133
  • 1
  • 7
  • For clarification, with the success response you're looking for a progress update? It starts with (0/5) completed then when the first task completes you want (1/5) completed...etc. Or do you want to keep track of how many Api calls actually succeeded/failed? –  Jun 03 '19 at 13:02
  • This seems like a case for asynchronous foreach (support is added in c# 8.0 only): https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/async-streams Isn't this something like what you are looking for? – iSpain17 Jun 03 '19 at 13:03

4 Answers4

4

In my app I need to update details based on the success response.

The proper way to do this is IProgress<string>. The calling code should supply a Progress<string> that updates the UI accordingly.

public async Task<Response> GetAllVasInformationAsync(IProgress<string> progress)
{
  var userDetails = UpdateWhenComplete(GetUserDetailsAsync(), "user details");
  var getWageInfo = UpdateWhenComplete(GetUserWageInfoAsync(), "wage information");
  var getSalaryInfo = UpdateWhenComplete(GetSalaryInfoAsync(), "salary information");

  await Task.WhenAll(userDetails, getWageInfo, getSalaryInfo);

  return new Response
  {
    IsuserDetailsSucceeded = await userDetails,
    IsgetWageInfoSucceeded = await getWageInfo,
    IsgetSalaryInfoSucceeded = await getSalaryInfo,
  };

  async Task<T> UpdateWhenComplete<T>(Task<T> task, string taskName)
  {
    try { return await task; }
    finally { progress?.Report($"Completed {taskName}"); }
  }
}

If you also need a count, you can either use IProgress<(int, string)> or change how the report progress string is built to include the count.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
2

So here's what I would do in C# 8 and .NET Standard 2.1:

First, I create the method which will produce the async enumerable:

static async IAsyncEnumerable<bool> TasksToPerform() {
    Task[] tasks = new Task[3] { userDetails, getWageInfo, getSalaryInfo };

    for (i = 0; i < tasks.Length; i++) {
        await tasks[i];
        yield return true;
    }
}

So now you need to await foreach on this task enumerable. Every time you get a return, you know that a task has been finished.

int numberOfFinishedTasks = 0;

await foreach (var b in TasksToPerform()) {
    numberOfFinishedTasks++;
    //Update UI here to reflect the finished task number
}
iSpain17
  • 2,502
  • 3
  • 17
  • 26
  • But in this case, how i get to know which task is failed and which succeeded ?? It is just tell weather the task has been completed or not right? Is there any way i can get some insight about the failed & succeeded status as well. – Sooraj Jun 03 '19 at 13:36
  • In that case, the tasks themselves need to return some kind of information about whether they failed or not. So they won't be of type `Task`, but for example `Task`, returning true if they succeeded, false if not. Or they could return a custom type, which has info on failure. – iSpain17 Jun 03 '19 at 13:42
  • I think IAsyncEnumerable is not available in Xamarin Forms https://stackoverflow.com/questions/55821746/is-iasyncenumerable-supported-in-c-sharp-8-0 – Rakesh R Nair Jun 03 '19 at 13:56
  • I did not know about that! But getting the preview, you should be able to use it, shouldn't you? – iSpain17 Jun 03 '19 at 14:07
1

.WhenAll() will allow you to determine if /any/ of the tasks failed, they you just count the tasks that have failed.

public async Task<Response> GetAllVasInformationAsync()
{
    var userDetails = GetUserDetailsAsync();
    var getWageInfo = GetUserWageInfoAsync();
    var getSalaryInfo = GetSalaryInfoAsync();


    await Task.WhenAll(userDetails, getWaitInfo, getSalaryInfo)
       .ContinueWith((task) =>
       {
          if(task.IsFaulted)
          { 
             int failedCount = 0;
             if(userDetails.IsFaulted) failedCount++;
             if(getWaitInfo.IsFaulted) failedCount++;
             if(getSalaryInfo.IsFaulted) failedCount++;
             return $"{failedCount} tasks failed";
          }
    });

    var resultToReturn = new Response
    {
        IsuserDetailsSucceeded = userDetails.Result,
        IsgetWageInfoSucceeded = getWageInfo.Result,
        IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
    };

    return resultToReturn;
}
Neil
  • 11,059
  • 3
  • 31
  • 56
1

No need to over-complicate this. This code will show how many of your tasks had exceptions. Your await task.whenall just triggers them and waits for them to finish. So after that you can do whatever you want with the tasks :)

var task = Task.Delay(300);
var tasks = new List<Task> { task };
var faultedTasks = 0;

tasks.ForEach(t =>
{
     t.ContinueWith(t2 =>
     {
          //do something with a field / property holding ViewModel state
          //that your view is listening to
     });
});

await Task.WhenAll(tasks);

//use this to respond with a finished count
tasks.ForEach(_ => { if (_.IsFaulted) faultedTasks++; });

Console.WriteLine($"{tasks.Count() - faultedTasks} / {tasks.Count()} completed.");

Rick Penabella
  • 339
  • 2
  • 10
  • 1
    You are waiting for all Tasks to end in this answer. That is not what the op is looking for. He looks to see how many of the started tasks has completed already. – iSpain17 Jun 03 '19 at 13:08
  • You're right so I edited it to account for viewing things as they happen :) – Rick Penabella Jun 03 '19 at 13:19