-1

I have created a C# Restsharp client to make queries towards my API using synchronous calls. The synchronous calls work as they should, however my asynchronous calls never return anything. The await operator should suspend the evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation. However, in my case, this never happends. I have searched through the whole of stack overflow now, but I cannot figure out why my async calls won't return anything. Can anyone spot what may cause the issues and how I can fix it? This is my setup:

The client side method:

public class Objects{

    private readonly IRestClient _restClient;

    public Objects(IRestClient restClient)
    {
        _restClient = restClient;
    }

    public async Task< List<ObjectGet> CreateObjects(List<ObjectBase>> objects, Boolean createCdfFile = true)
    {

        var request = new RestRequest("/objects/", Method.POST) { RequestFormat = DataFormat.Json };

        var objectsJson = JsonConvert.SerializeObject(objects, Formatting.Indented);
        request.AddParameter("create_cdf_file", createCdfFile, ParameterType.QueryString)
               .AddParameter("application/json", objectsJson, ParameterType.RequestBody);

        IRestResponse<List<ObjectGet>> response = await _restClient.ExecuteAsync<List<ObjectGet>>(request);

        return response.Data;
    }
}

My synchronous call works:

var obj = _restClient.FetchAllObjects(filter).GetAwaiter().GetResult();

returns : System.Collections.Generic.List1[RestsharpApiClient.model.ObjectWithRelationships]

My asynchronous call executes, but..

var obj = _restClient.FetchAllObjects(filter);

never returns anything:

However, if run the async call before the synchronous call in sequence, they both return the right result:

var obj = _restClient.FetchAllObjects(filter);
var objAsync = _restClient.FetchAllObjects(filter).GetAwaiter().GetResult();

returns:

System.Collections.Generic.List1[RestsharpApiClient.model.ObjectWithRelationships] , System.Collections.Generic.List1[RestsharpApiClient.model.ObjectWithRelationships]

This is my projects file

TargetFramework=netstandard2.0
<LangVersion>8.0</LangVersion>
"Newtonsoft.Json" Version="12.0.3"
"RestSharp" Version="106.11.4"

ANY HELP WOULD BE VERY MUCH APPRECIATED

  • 1
    with `var obj = _restClient.FetchAllObjects(filter);` obj is `Task>` not `List` – Selvin Jul 27 '20 at 14:38
  • 3
    `var obj = _restClient.FetchAllObjects(filter);` is a [fire and forget](https://stackoverflow.com/q/46053175/11683). You start the task and carry on before it has a chance to return. When you place a synchronous call after it, you give it the time to return and you observe the result. If you want the result, you should be doing `var obj = await _restClient.FetchAllObjects(filter);` instead. You should [not be doing](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) `.GetAwaiter().GetResult()` either. – GSerg Jul 27 '20 at 14:39
  • @Selvin @GSerg I tried the following: `List < ObjectWithRelationships > obj = await _restClient.FetchAllObjects(filter);` and then print `Console.WriteLine(obj);` but the query still returns nothing. Any idea? – Kristoffer Rakstad Solberg Jul 27 '20 at 14:51
  • How do you know it returns nothing? Because it printed ```System.Collections.Generic.List`1[ObjectWithRelationships]```? – GSerg Jul 27 '20 at 14:55
  • @GSerg Sorry I was a bit unclear. When debugging, at runtime the code is never able to pass `IRestResponse> response = await _restClient.ExecuteAsync>(request);`. It tries to execute and exites with code 0 at this line. So to be a bit more specific, the Console.Writeline(obj) is actually never reached. – Kristoffer Rakstad Solberg Jul 27 '20 at 15:02

2 Answers2

1

From what I can deduce from the comments, you have something like this:

static void Main(string[] args)
{
    Program prog = new Program();
    prog.OnStart();
}

class Program
{
    private async void OnStart()
    {
        var obj = await _restClient.FetchAllObjects(filter);
        Console.Writeline(obj);
    }
}

FetchAllObjects returns Task<List<ObjectWithRelationships>>, so by using await, the resulting List<ObjectWithRelationships> will be unwrapped when the Task completes, making obj of type List<ObjectWithRelationships>.

However, using await also causes your OnStart method to return, scheduling a continuation, and because it is declared void, your Main method doesn't know it needs to wait and will proceed to exit before FetchAllObjects has completed.

You need to return a Task from OnStart:

private async Task OnStart()

Now when await is hit, OnStart will return a Task to Main.

Your Main method can now wait for that Task to complete:

static void Main(string[] args)
{
    Program prog = new Program();
    prog.OnStart().GetAwaiter().GetResult();
}

And if you're using C#7.1+, you can make use of async Main:

static async Task Main(string[] args)
{
    Program prog = new Program();
    await prog.OnStart();
}

Which is simply syntactic sugar for:

static void Main(string[] args) => MainAsync(args).GetAwaiter.GetResult();

private static async Task MainAsync(string[] args)
{
    Program prog = new Program();
    await prog.OnStart();
}
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
0

It tries to execute and exites [sic] with code 0 at this line.

Well of course, once a continuation point is reached the program flow is returned to your calling function and the rest of the code does something (in your case, most likely it's using a thread pool thread to build the response).

And what happens when you reach the end of Main? Your program exits.

Now of course you don't show how you call it, but from the code patterns you do show I'm positive you're using the synchronous async anti-pattern. Don't. Ever. Declare your Main async and properly await your calls and you'll get what you want.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • I have declared my main as `static void Main(string[] args) { Program prog = new Program(); }` and then inside the Main I call my method `prog.OnStart()`. OnStart() is declared as `private async void OnStart() {var obj = _restClient.FetchAllObjects(filter); Console.Writeline(obj);}` – Kristoffer Rakstad Solberg Jul 27 '20 at 15:19
  • Exactly, which is incorrect from beginning to end. All async methods need to return `Task`, and that includes your `OnStart` and `Main`, and once you do that you'll get a whole bunch of warnings about not awaiting tasks. Plus you can't await methods in your constructor, so that's wrong too. – Blindy Jul 27 '20 at 15:23